monsqlize 3.3.3 → 3.4.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/CHANGELOG.md +5 -3
- package/README.md +1 -1
- package/changelogs/v3.3.4.md +21 -0
- package/changelogs/v3.4.0.md +28 -0
- package/dist/cjs/cli/data-task.cjs +8 -5
- package/dist/cjs/index.cjs +594 -100
- package/dist/cjs/transaction/Transaction.cjs +7 -0
- package/dist/cjs/transaction/TransactionManager.cjs +7 -0
- package/dist/esm/index.mjs +594 -100
- package/dist/types/base.d.mts +6 -0
- package/dist/types/base.d.ts +6 -0
- package/dist/types/collection.d.mts +2 -2
- package/dist/types/collection.d.ts +2 -2
- package/dist/types/index.d.mts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/model.d.mts +2 -2
- package/dist/types/model.d.ts +2 -2
- package/dist/types/runtime.d.mts +7 -0
- package/dist/types/runtime.d.ts +7 -0
- package/dist/types/search-index.d.mts +107 -0
- package/dist/types/search-index.d.ts +107 -0
- package/package.json +8 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -83,6 +83,13 @@ var ErrorCodes = {
|
|
|
83
83
|
INVALID_OPERATION: "INVALID_OPERATION",
|
|
84
84
|
/** Invalid local Vector Search options before the driver is called. */
|
|
85
85
|
INVALID_VECTOR_SEARCH: "INVALID_VECTOR_SEARCH",
|
|
86
|
+
/** Search index lifecycle errors (v3.4.0). */
|
|
87
|
+
INVALID_SEARCH_INDEX_DEFINITION: "INVALID_SEARCH_INDEX_DEFINITION",
|
|
88
|
+
SEARCH_INDEX_EXISTS: "SEARCH_INDEX_EXISTS",
|
|
89
|
+
SEARCH_INDEX_NOT_FOUND: "SEARCH_INDEX_NOT_FOUND",
|
|
90
|
+
SEARCH_INDEX_UNSUPPORTED: "SEARCH_INDEX_UNSUPPORTED",
|
|
91
|
+
SEARCH_INDEX_BUILD_FAILED: "SEARCH_INDEX_BUILD_FAILED",
|
|
92
|
+
SEARCH_INDEX_WAIT_TIMEOUT: "SEARCH_INDEX_WAIT_TIMEOUT",
|
|
86
93
|
/** Relation select is outside the supported inclusion-only top-level contract. */
|
|
87
94
|
INVALID_RELATION_SELECT: "INVALID_RELATION_SELECT",
|
|
88
95
|
/** A protected delete found declared Model relations that still reference its target. */
|
|
@@ -1015,31 +1022,31 @@ function normalizeRelationSelect(value, context = {}) {
|
|
|
1015
1022
|
if (value === void 0) {
|
|
1016
1023
|
return void 0;
|
|
1017
1024
|
}
|
|
1018
|
-
const
|
|
1025
|
+
const invalid3 = (reason) => {
|
|
1019
1026
|
throw createError(
|
|
1020
1027
|
ErrorCodes.INVALID_RELATION_SELECT,
|
|
1021
1028
|
`Relation select must use non-empty top-level inclusion fields: ${reason}`,
|
|
1022
1029
|
[{ ...context, value, reason }]
|
|
1023
1030
|
);
|
|
1024
1031
|
};
|
|
1025
|
-
const rawTokens = typeof value === "string" ? [value] : Array.isArray(value) ? value :
|
|
1032
|
+
const rawTokens = typeof value === "string" ? [value] : Array.isArray(value) ? value : invalid3("select must be a string or string array");
|
|
1026
1033
|
const tokens = [];
|
|
1027
1034
|
for (const rawToken of rawTokens) {
|
|
1028
1035
|
if (typeof rawToken !== "string") {
|
|
1029
|
-
|
|
1036
|
+
invalid3("select array entries must be strings");
|
|
1030
1037
|
}
|
|
1031
1038
|
const parts = rawToken.trim().split(/\s+/).filter(Boolean);
|
|
1032
1039
|
if (parts.length === 0) {
|
|
1033
|
-
|
|
1040
|
+
invalid3("select must not be empty");
|
|
1034
1041
|
}
|
|
1035
1042
|
tokens.push(...parts);
|
|
1036
1043
|
}
|
|
1037
1044
|
for (const token of tokens) {
|
|
1038
1045
|
if (token.startsWith("-") || token.startsWith("+")) {
|
|
1039
|
-
|
|
1046
|
+
invalid3("exclusion and prefix tokens are not supported");
|
|
1040
1047
|
}
|
|
1041
1048
|
if (token.includes(".")) {
|
|
1042
|
-
|
|
1049
|
+
invalid3("dot-path fields are not supported");
|
|
1043
1050
|
}
|
|
1044
1051
|
}
|
|
1045
1052
|
return [...new Set(tokens)];
|
|
@@ -3594,8 +3601,8 @@ function normalizeExtensionValue(value, stack) {
|
|
|
3594
3601
|
return `circular:${getExtensionObjectKey(objectValue)}`;
|
|
3595
3602
|
}
|
|
3596
3603
|
const prototype = Object.getPrototypeOf(objectValue);
|
|
3597
|
-
const
|
|
3598
|
-
if (!Array.isArray(objectValue) && !
|
|
3604
|
+
const isPlainObject5 = prototype === Object.prototype || prototype === null;
|
|
3605
|
+
if (!Array.isArray(objectValue) && !isPlainObject5) {
|
|
3599
3606
|
return `object:${prototype?.constructor?.name ?? "unknown"}:${getExtensionObjectKey(objectValue)}`;
|
|
3600
3607
|
}
|
|
3601
3608
|
stack.add(objectValue);
|
|
@@ -6709,6 +6716,28 @@ var ModelInstance = class {
|
|
|
6709
6716
|
dropIndexes() {
|
|
6710
6717
|
return this.runModelWrite(() => this.collection.dropIndexes());
|
|
6711
6718
|
}
|
|
6719
|
+
/** Search index lifecycle (v3.4.0): writes run as model writes for writePathPolicy. */
|
|
6720
|
+
createSearchIndex(spec, options) {
|
|
6721
|
+
return this.runModelWrite(() => this.collection.createSearchIndex(spec, options));
|
|
6722
|
+
}
|
|
6723
|
+
createSearchIndexes(specs) {
|
|
6724
|
+
return this.runModelWrite(() => this.collection.createSearchIndexes(specs));
|
|
6725
|
+
}
|
|
6726
|
+
listSearchIndexes(options) {
|
|
6727
|
+
return this.collection.listSearchIndexes(options);
|
|
6728
|
+
}
|
|
6729
|
+
getSearchIndex(name) {
|
|
6730
|
+
return this.collection.getSearchIndex(name);
|
|
6731
|
+
}
|
|
6732
|
+
updateSearchIndex(name, definition) {
|
|
6733
|
+
return this.runModelWrite(() => this.collection.updateSearchIndex(name, definition));
|
|
6734
|
+
}
|
|
6735
|
+
dropSearchIndex(name, options) {
|
|
6736
|
+
return this.runModelWrite(() => this.collection.dropSearchIndex(name, options));
|
|
6737
|
+
}
|
|
6738
|
+
waitForSearchIndex(name, options) {
|
|
6739
|
+
return this.collection.waitForSearchIndex(name, options);
|
|
6740
|
+
}
|
|
6712
6741
|
prewarmBookmarks(keyDims, pages) {
|
|
6713
6742
|
return this.extendedCollection().prewarmBookmarks(keyDims, pages);
|
|
6714
6743
|
}
|
|
@@ -8265,18 +8294,26 @@ var HealthChecker = class {
|
|
|
8265
8294
|
const status = this._healthStatus.get(poolName);
|
|
8266
8295
|
if (!status) return;
|
|
8267
8296
|
const client = this._clients.get(poolName) ?? this._poolManager?._getPool(poolName);
|
|
8268
|
-
|
|
8297
|
+
const pending = this._inProgress.get(poolName);
|
|
8298
|
+
if (pending && pending.client === client && pending.awaited) return;
|
|
8269
8299
|
const epoch = this._epochs.get(poolName);
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
}
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8300
|
+
let operation;
|
|
8301
|
+
if (pending && pending.client === client) {
|
|
8302
|
+
operation = pending;
|
|
8303
|
+
} else {
|
|
8304
|
+
const promise2 = Promise.resolve().then(() => {
|
|
8305
|
+
if (this._epochs.get(poolName) !== epoch || this._healthStatus.get(poolName) !== status) return;
|
|
8306
|
+
return this._pingOverride ? this._pingOverride(poolName) : this._pingPool(poolName);
|
|
8307
|
+
});
|
|
8308
|
+
operation = { client, promise: promise2, awaited: true };
|
|
8309
|
+
this._inProgress.set(poolName, operation);
|
|
8310
|
+
const clearPending = () => {
|
|
8311
|
+
if (this._inProgress.get(poolName) === operation) this._inProgress.delete(poolName);
|
|
8312
|
+
};
|
|
8313
|
+
void promise2.then(clearPending, clearPending);
|
|
8314
|
+
}
|
|
8315
|
+
operation.awaited = true;
|
|
8316
|
+
const promise = operation.promise;
|
|
8280
8317
|
let timer;
|
|
8281
8318
|
let lastError = null;
|
|
8282
8319
|
try {
|
|
@@ -8288,6 +8325,7 @@ var HealthChecker = class {
|
|
|
8288
8325
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
8289
8326
|
} finally {
|
|
8290
8327
|
if (timer) clearTimeout(timer);
|
|
8328
|
+
operation.awaited = false;
|
|
8291
8329
|
}
|
|
8292
8330
|
if (this._epochs.get(poolName) !== epoch || this._healthStatus.get(poolName) !== status) return;
|
|
8293
8331
|
status.lastCheck = /* @__PURE__ */ new Date();
|
|
@@ -10745,27 +10783,28 @@ function normalizeFindPageLimit(limit, defaults2) {
|
|
|
10745
10783
|
}
|
|
10746
10784
|
return rawLimit;
|
|
10747
10785
|
}
|
|
10748
|
-
function documentReadKey(operation, collection, defaults2, query, options, field) {
|
|
10749
|
-
const
|
|
10786
|
+
function documentReadKey(operation, collection, defaults2, query, options, field, queryPrepared = false) {
|
|
10787
|
+
const keyQuery = queryPrepared ? query ?? {} : normalizeQueryForCache(query, defaults2);
|
|
10788
|
+
const captured = snapshotReadInputs(keyQuery, options, collection.bsonOptions);
|
|
10750
10789
|
const keyOptions = buildResultCacheKeyOptions(captured.options, collection.bsonOptions);
|
|
10751
10790
|
if (operation === "count") delete keyOptions.fieldsAsRaw;
|
|
10752
10791
|
const body = field === void 0 ? stableCacheKeyString(captured.query, "document") : stableCacheKeyString({ key: field, query: captured.query });
|
|
10753
10792
|
return `${operation}:${buildCollectionCacheNamespace(collection, defaults2)}:${body}:${stableCacheKeyString(keyOptions)}`;
|
|
10754
10793
|
}
|
|
10755
|
-
function buildFindCacheKey(collection, defaults2 = {}, query, options) {
|
|
10794
|
+
function buildFindCacheKey(collection, defaults2 = {}, query, options, queryPrepared = false) {
|
|
10756
10795
|
return documentReadKey("find", collection, defaults2, query, normalizeFindProjectionOptions({
|
|
10757
10796
|
limit: defaults2.findLimit,
|
|
10758
10797
|
...options ?? {}
|
|
10759
|
-
}));
|
|
10798
|
+
}), void 0, queryPrepared);
|
|
10760
10799
|
}
|
|
10761
|
-
function buildFindOneCacheKey(collection, defaults2 = {}, query, options) {
|
|
10762
|
-
return documentReadKey("findOne", collection, defaults2, query, normalizeFindProjectionOptions(options));
|
|
10800
|
+
function buildFindOneCacheKey(collection, defaults2 = {}, query, options, queryPrepared = false) {
|
|
10801
|
+
return documentReadKey("findOne", collection, defaults2, query, normalizeFindProjectionOptions(options), void 0, queryPrepared);
|
|
10763
10802
|
}
|
|
10764
|
-
function buildCountCacheKey(collection, defaults2 = {}, query, options) {
|
|
10765
|
-
return documentReadKey("count", collection, defaults2, query, options);
|
|
10803
|
+
function buildCountCacheKey(collection, defaults2 = {}, query, options, queryPrepared = false) {
|
|
10804
|
+
return documentReadKey("count", collection, defaults2, query, options, void 0, queryPrepared);
|
|
10766
10805
|
}
|
|
10767
|
-
function buildDistinctCacheKey(collection, defaults2 = {}, field, query, options) {
|
|
10768
|
-
return documentReadKey("distinct", collection, defaults2, query, options, field);
|
|
10806
|
+
function buildDistinctCacheKey(collection, defaults2 = {}, field, query, options, queryPrepared = false) {
|
|
10807
|
+
return documentReadKey("distinct", collection, defaults2, query, options, field, queryPrepared);
|
|
10769
10808
|
}
|
|
10770
10809
|
function buildPreparedAggregateCacheKey(collection, defaults2 = {}, pipeline = [], options) {
|
|
10771
10810
|
const namespace = buildCollectionCacheNamespace(collection, defaults2);
|
|
@@ -11777,7 +11816,7 @@ var FindChain = class {
|
|
|
11777
11816
|
const cacheTTL = typeof this.options.cache === "number" ? this.options.cache : 0;
|
|
11778
11817
|
const executeOptions = prepared.options;
|
|
11779
11818
|
if (cacheTTL > 0 && this.queryCache && canCacheBsonResult(executeOptions) && !hasSessionOption(executeOptions) && !await isCollectionCacheBarrierActive(this.queryCache, this.collection, this.defaults)) {
|
|
11780
|
-
const cacheKey = buildFindCacheKey(this.collection, this.defaults, prepared.query, executeOptions);
|
|
11819
|
+
const cacheKey = buildFindCacheKey(this.collection, this.defaults, prepared.query, executeOptions, true);
|
|
11781
11820
|
const result = await readThroughCache(this.queryCache, cacheKey, cacheTTL, () => this.runToArray(prepared));
|
|
11782
11821
|
return this.wrapResult("find", startTs, result);
|
|
11783
11822
|
}
|
|
@@ -12118,6 +12157,432 @@ function mapVectorSearchRows(rows) {
|
|
|
12118
12157
|
});
|
|
12119
12158
|
}
|
|
12120
12159
|
|
|
12160
|
+
// src/adapters/mongodb/search-indexes/errors.ts
|
|
12161
|
+
var CODE_INDEX_ALREADY_EXISTS = 68;
|
|
12162
|
+
var CODE_INDEX_NOT_FOUND = 27;
|
|
12163
|
+
var CODE_NAMESPACE_NOT_FOUND = 26;
|
|
12164
|
+
var CODE_COMMAND_NOT_FOUND = 59;
|
|
12165
|
+
var CODE_COMMAND_NOT_SUPPORTED = 115;
|
|
12166
|
+
var CODE_LIST_SEARCH_ATLAS_ONLY = 6047401;
|
|
12167
|
+
var UNSUPPORTED_MESSAGE = /search index commands are only supported|\$listSearchIndexes stage is only allowed/i;
|
|
12168
|
+
function readCode(error) {
|
|
12169
|
+
const e = error;
|
|
12170
|
+
return {
|
|
12171
|
+
code: typeof e?.code === "number" ? e.code : void 0,
|
|
12172
|
+
codeName: typeof e?.codeName === "string" ? e.codeName : void 0,
|
|
12173
|
+
message: typeof e?.message === "string" ? e.message : String(error)
|
|
12174
|
+
};
|
|
12175
|
+
}
|
|
12176
|
+
function classifySearchIndexError(error) {
|
|
12177
|
+
const { code, codeName, message } = readCode(error);
|
|
12178
|
+
if (code === CODE_INDEX_ALREADY_EXISTS || codeName === "IndexAlreadyExists") return "exists";
|
|
12179
|
+
if (code === CODE_NAMESPACE_NOT_FOUND || codeName === "NamespaceNotFound") return "namespaceNotFound";
|
|
12180
|
+
if (code === CODE_INDEX_NOT_FOUND || codeName === "IndexNotFound") return "notFound";
|
|
12181
|
+
if (code === CODE_COMMAND_NOT_FOUND || code === CODE_COMMAND_NOT_SUPPORTED || code === CODE_LIST_SEARCH_ATLAS_ONLY || codeName === "CommandNotFound" || codeName === "CommandNotSupported" || codeName === "SearchNotEnabled") return "unsupported";
|
|
12182
|
+
if (code === void 0 && codeName === void 0 && /already exists/i.test(message)) return "exists";
|
|
12183
|
+
if (UNSUPPORTED_MESSAGE.test(message)) return "unsupported";
|
|
12184
|
+
return "other";
|
|
12185
|
+
}
|
|
12186
|
+
function attachContext(error, ctx) {
|
|
12187
|
+
const contextualized = error;
|
|
12188
|
+
try {
|
|
12189
|
+
contextualized.monsqlize = {
|
|
12190
|
+
...contextualized.monsqlize && typeof contextualized.monsqlize === "object" ? contextualized.monsqlize : {},
|
|
12191
|
+
operation: ctx.operation,
|
|
12192
|
+
namespace: ctx.namespace,
|
|
12193
|
+
indexName: ctx.indexName
|
|
12194
|
+
};
|
|
12195
|
+
} catch {
|
|
12196
|
+
}
|
|
12197
|
+
return error;
|
|
12198
|
+
}
|
|
12199
|
+
function searchIndexError(code, message, ctx, extra = {}, cause) {
|
|
12200
|
+
return createError(code, message, [{ operation: ctx.operation, namespace: ctx.namespace, indexName: ctx.indexName, ...extra }], cause);
|
|
12201
|
+
}
|
|
12202
|
+
function mapSearchIndexError(error, ctx) {
|
|
12203
|
+
if (!(error instanceof Error)) {
|
|
12204
|
+
return createError(ErrorCodes.MONGODB_ERROR, `${ctx.operation} failed`, [{ ...ctx, reason: String(error) }]);
|
|
12205
|
+
}
|
|
12206
|
+
if (error.code !== void 0 && typeof error.code === "string" && String(error.code).startsWith("SEARCH_INDEX_")) {
|
|
12207
|
+
return error;
|
|
12208
|
+
}
|
|
12209
|
+
const { code, codeName, message } = readCode(error);
|
|
12210
|
+
const server = { serverCode: code, serverCodeName: codeName, serverMessage: message };
|
|
12211
|
+
switch (classifySearchIndexError(error)) {
|
|
12212
|
+
case "exists":
|
|
12213
|
+
return searchIndexError(ErrorCodes.SEARCH_INDEX_EXISTS, `Search index "${ctx.indexName}" already exists`, ctx, server, error);
|
|
12214
|
+
case "notFound":
|
|
12215
|
+
return searchIndexError(ErrorCodes.SEARCH_INDEX_NOT_FOUND, `Search index "${ctx.indexName}" was not found`, ctx, server, error);
|
|
12216
|
+
case "unsupported":
|
|
12217
|
+
return searchIndexError(ErrorCodes.SEARCH_INDEX_UNSUPPORTED, "This deployment does not support Search index commands (requires Atlas, MongoDB 8.2+ with mongot, or mongodb-atlas-local)", ctx, server, error);
|
|
12218
|
+
default:
|
|
12219
|
+
return attachContext(searchIndexError(ErrorCodes.MONGODB_ERROR, `${ctx.operation} failed: ${message}`, ctx, server, error), ctx);
|
|
12220
|
+
}
|
|
12221
|
+
}
|
|
12222
|
+
|
|
12223
|
+
// src/adapters/mongodb/search-indexes/normalize.ts
|
|
12224
|
+
function missingSearchIndexInfo(name) {
|
|
12225
|
+
return { name, status: "DOES_NOT_EXIST", queryable: false, typeSource: "unknown", raw: {} };
|
|
12226
|
+
}
|
|
12227
|
+
function normalizeSearchIndexInfo(doc) {
|
|
12228
|
+
const info = {
|
|
12229
|
+
name: String(doc.name),
|
|
12230
|
+
// Unknown future statuses are passed through verbatim rather than rewritten.
|
|
12231
|
+
status: typeof doc.status === "string" && doc.status.length > 0 ? doc.status : "PENDING",
|
|
12232
|
+
queryable: doc.queryable === true,
|
|
12233
|
+
typeSource: "unknown",
|
|
12234
|
+
raw: doc
|
|
12235
|
+
};
|
|
12236
|
+
if (typeof doc.id === "string") info.id = doc.id;
|
|
12237
|
+
const resolved = resolveSearchIndexType(doc);
|
|
12238
|
+
info.typeSource = resolved.typeSource;
|
|
12239
|
+
if (resolved.type !== void 0) info.type = resolved.type;
|
|
12240
|
+
if (doc.latestDefinition && typeof doc.latestDefinition === "object") info.latestDefinition = doc.latestDefinition;
|
|
12241
|
+
const version = doc.latestDefinitionVersion;
|
|
12242
|
+
if (version && typeof version === "object") {
|
|
12243
|
+
info.latestDefinitionVersion = { version: Number(version.version), createdAt: version.createdAt };
|
|
12244
|
+
}
|
|
12245
|
+
if (Array.isArray(doc.statusDetail)) info.statusDetail = doc.statusDetail;
|
|
12246
|
+
return info;
|
|
12247
|
+
}
|
|
12248
|
+
function resolveSearchIndexType(doc) {
|
|
12249
|
+
if (doc.type === "search" || doc.type === "vectorSearch") return { type: doc.type, typeSource: "server" };
|
|
12250
|
+
const definition = doc.latestDefinition;
|
|
12251
|
+
if (definition && typeof definition === "object" && !Array.isArray(definition)) {
|
|
12252
|
+
const fields = definition.fields;
|
|
12253
|
+
if (Array.isArray(fields) && fields.some((field) => field && typeof field === "object" && field.type === "vector")) {
|
|
12254
|
+
return { type: "vectorSearch", typeSource: "inferred" };
|
|
12255
|
+
}
|
|
12256
|
+
if (Object.prototype.hasOwnProperty.call(definition, "mappings")) return { type: "search", typeSource: "inferred" };
|
|
12257
|
+
}
|
|
12258
|
+
return { typeSource: "unknown" };
|
|
12259
|
+
}
|
|
12260
|
+
function matchesSearchIndexType(info, type) {
|
|
12261
|
+
return info.typeSource !== "unknown" && info.type === type;
|
|
12262
|
+
}
|
|
12263
|
+
|
|
12264
|
+
// src/adapters/mongodb/search-indexes/validate.ts
|
|
12265
|
+
var TYPES = /* @__PURE__ */ new Set(["search", "vectorSearch"]);
|
|
12266
|
+
var VECTOR_FIELD_TYPES = /* @__PURE__ */ new Set(["vector", "filter"]);
|
|
12267
|
+
var SIMILARITIES = /* @__PURE__ */ new Set(["euclidean", "cosine", "dotProduct"]);
|
|
12268
|
+
function invalid(operation, namespace, indexName, field, reason) {
|
|
12269
|
+
throw createError(
|
|
12270
|
+
ErrorCodes.INVALID_SEARCH_INDEX_DEFINITION,
|
|
12271
|
+
`${operation}: invalid ${field} (${reason})`,
|
|
12272
|
+
[{ operation, namespace, indexName: typeof indexName === "string" ? indexName : void 0, field, reason }]
|
|
12273
|
+
);
|
|
12274
|
+
}
|
|
12275
|
+
function isPlainObject4(value) {
|
|
12276
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
12277
|
+
const proto = Object.getPrototypeOf(value);
|
|
12278
|
+
return proto === Object.prototype || proto === null;
|
|
12279
|
+
}
|
|
12280
|
+
function assertIndexName(operation, namespace, name) {
|
|
12281
|
+
if (typeof name !== "string" || name.length === 0 || name.trim() !== name) {
|
|
12282
|
+
invalid(operation, namespace, name, "name", "non-empty string without leading or trailing whitespace required");
|
|
12283
|
+
}
|
|
12284
|
+
}
|
|
12285
|
+
function assertDefinition(operation, namespace, name, definition, type) {
|
|
12286
|
+
if (!isPlainObject4(definition)) invalid(operation, namespace, name, "definition", "plain object required");
|
|
12287
|
+
if (type !== "vectorSearch") return;
|
|
12288
|
+
const fields = definition.fields;
|
|
12289
|
+
if (!Array.isArray(fields) || fields.length === 0) {
|
|
12290
|
+
invalid(operation, namespace, name, "definition.fields", "non-empty array required for vectorSearch");
|
|
12291
|
+
}
|
|
12292
|
+
fields.forEach((field, index) => {
|
|
12293
|
+
const path5 = `definition.fields[${index}]`;
|
|
12294
|
+
if (!isPlainObject4(field)) invalid(operation, namespace, name, path5, "object required");
|
|
12295
|
+
if (typeof field.type !== "string" || !VECTOR_FIELD_TYPES.has(field.type)) {
|
|
12296
|
+
invalid(operation, namespace, name, `${path5}.type`, 'must be "vector" or "filter"');
|
|
12297
|
+
}
|
|
12298
|
+
if (typeof field.path !== "string" || field.path.length === 0) {
|
|
12299
|
+
invalid(operation, namespace, name, `${path5}.path`, "non-empty string required");
|
|
12300
|
+
}
|
|
12301
|
+
if (field.type === "vector") {
|
|
12302
|
+
const dims = field.numDimensions;
|
|
12303
|
+
if (typeof dims !== "number" || !Number.isInteger(dims) || dims <= 0) {
|
|
12304
|
+
invalid(operation, namespace, name, `${path5}.numDimensions`, "positive integer required");
|
|
12305
|
+
}
|
|
12306
|
+
if (typeof field.similarity !== "string" || !SIMILARITIES.has(field.similarity)) {
|
|
12307
|
+
invalid(operation, namespace, name, `${path5}.similarity`, 'must be "euclidean", "cosine", or "dotProduct"');
|
|
12308
|
+
}
|
|
12309
|
+
}
|
|
12310
|
+
});
|
|
12311
|
+
}
|
|
12312
|
+
function assertSpec(operation, namespace, spec) {
|
|
12313
|
+
if (!isPlainObject4(spec)) invalid(operation, namespace, void 0, "spec", "object required");
|
|
12314
|
+
assertIndexName(operation, namespace, spec.name);
|
|
12315
|
+
if (typeof spec.type !== "string" || !TYPES.has(spec.type)) {
|
|
12316
|
+
invalid(operation, namespace, spec.name, "type", 'must be "search" or "vectorSearch"');
|
|
12317
|
+
}
|
|
12318
|
+
assertDefinition(operation, namespace, spec.name, spec.definition, spec.type);
|
|
12319
|
+
}
|
|
12320
|
+
function assertOptionsObject(operation, namespace, name, options) {
|
|
12321
|
+
if (options !== void 0 && !isPlainObject4(options)) invalid(operation, namespace, name, "options", "object required");
|
|
12322
|
+
}
|
|
12323
|
+
function assertEnumOption(operation, namespace, name, field, value, allowed) {
|
|
12324
|
+
if (value !== void 0 && (typeof value !== "string" || !allowed.includes(value))) {
|
|
12325
|
+
invalid(operation, namespace, name, field, `must be one of ${allowed.map((v) => `"${v}"`).join(", ")}`);
|
|
12326
|
+
}
|
|
12327
|
+
}
|
|
12328
|
+
function assertBooleanOption(operation, namespace, name, field, value) {
|
|
12329
|
+
if (value !== void 0 && typeof value !== "boolean") invalid(operation, namespace, name, field, "boolean required");
|
|
12330
|
+
}
|
|
12331
|
+
function resolveWaitOptions(namespace, name, options) {
|
|
12332
|
+
const operation = "waitForSearchIndex";
|
|
12333
|
+
assertOptionsObject(operation, namespace, name, options);
|
|
12334
|
+
const opts = options ?? {};
|
|
12335
|
+
assertEnumOption(operation, namespace, name, "options.until", opts.until, ["queryable", "ready", "dropped"]);
|
|
12336
|
+
const timeoutMs = opts.timeoutMs ?? 3e5;
|
|
12337
|
+
const intervalMs = opts.intervalMs ?? 2e3;
|
|
12338
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
|
|
12339
|
+
invalid(operation, namespace, name, "options.timeoutMs", "positive integer required");
|
|
12340
|
+
}
|
|
12341
|
+
if (!Number.isInteger(intervalMs) || intervalMs < 100) {
|
|
12342
|
+
invalid(operation, namespace, name, "options.intervalMs", "integer >= 100 required");
|
|
12343
|
+
}
|
|
12344
|
+
const signal = opts.signal;
|
|
12345
|
+
if (signal !== void 0 && (signal === null || typeof signal !== "object" || typeof signal.aborted !== "boolean" || typeof signal.addEventListener !== "function")) {
|
|
12346
|
+
invalid(operation, namespace, name, "options.signal", "AbortSignal required");
|
|
12347
|
+
}
|
|
12348
|
+
return { until: opts.until ?? "queryable", timeoutMs, intervalMs, signal };
|
|
12349
|
+
}
|
|
12350
|
+
|
|
12351
|
+
// src/adapters/mongodb/search-indexes/operations.ts
|
|
12352
|
+
var SEARCH_INDEX_MISSING_GRACE_MS = 3e4;
|
|
12353
|
+
async function fetchByName(ctx, operation, name, signal) {
|
|
12354
|
+
try {
|
|
12355
|
+
const docs = await ctx.collection.listSearchIndexes(name, signal ? { signal } : {}).toArray();
|
|
12356
|
+
const match = docs.find((doc) => doc.name === name);
|
|
12357
|
+
return match ? normalizeSearchIndexInfo(match) : missingSearchIndexInfo(name);
|
|
12358
|
+
} catch (error) {
|
|
12359
|
+
if (signal?.aborted) throw signal.reason;
|
|
12360
|
+
if (classifySearchIndexError(error) === "namespaceNotFound") return missingSearchIndexInfo(name);
|
|
12361
|
+
throw mapSearchIndexError(error, { operation, namespace: ctx.namespace, indexName: name });
|
|
12362
|
+
}
|
|
12363
|
+
}
|
|
12364
|
+
async function getSearchIndexOp(ctx, name) {
|
|
12365
|
+
assertIndexName("getSearchIndex", ctx.namespace, name);
|
|
12366
|
+
return fetchByName(ctx, "getSearchIndex", name);
|
|
12367
|
+
}
|
|
12368
|
+
async function listSearchIndexesOp(ctx, options) {
|
|
12369
|
+
const operation = "listSearchIndexes";
|
|
12370
|
+
assertOptionsObject(operation, ctx.namespace, void 0, options);
|
|
12371
|
+
assertEnumOption(operation, ctx.namespace, void 0, "options.type", options?.type, ["search", "vectorSearch"]);
|
|
12372
|
+
let docs;
|
|
12373
|
+
try {
|
|
12374
|
+
docs = await ctx.collection.listSearchIndexes().toArray();
|
|
12375
|
+
} catch (error) {
|
|
12376
|
+
if (classifySearchIndexError(error) === "namespaceNotFound") return [];
|
|
12377
|
+
throw mapSearchIndexError(error, { operation, namespace: ctx.namespace });
|
|
12378
|
+
}
|
|
12379
|
+
const infos = docs.map(normalizeSearchIndexInfo);
|
|
12380
|
+
return options?.type ? infos.filter((info) => matchesSearchIndexType(info, options.type)) : infos;
|
|
12381
|
+
}
|
|
12382
|
+
async function createSearchIndexOp(ctx, spec, options) {
|
|
12383
|
+
const operation = "createSearchIndex";
|
|
12384
|
+
assertSpec(operation, ctx.namespace, spec);
|
|
12385
|
+
assertOptionsObject(operation, ctx.namespace, spec.name, options);
|
|
12386
|
+
assertEnumOption(operation, ctx.namespace, spec.name, "options.ifExists", options?.ifExists, ["error", "skip", "update"]);
|
|
12387
|
+
const ifExists = options?.ifExists ?? "error";
|
|
12388
|
+
const errCtx = { operation, namespace: ctx.namespace, indexName: spec.name };
|
|
12389
|
+
const existing = await fetchByName(ctx, operation, spec.name);
|
|
12390
|
+
if (existing.status !== "DOES_NOT_EXIST") {
|
|
12391
|
+
if (ifExists === "skip") return { name: spec.name, action: "skipped", created: false };
|
|
12392
|
+
if (ifExists === "error") {
|
|
12393
|
+
throw searchIndexError(ErrorCodes.SEARCH_INDEX_EXISTS, `Search index "${spec.name}" already exists`, errCtx);
|
|
12394
|
+
}
|
|
12395
|
+
if (existing.type !== void 0 && existing.type !== spec.type) {
|
|
12396
|
+
throw searchIndexError(
|
|
12397
|
+
ErrorCodes.INVALID_SEARCH_INDEX_DEFINITION,
|
|
12398
|
+
`Search index "${spec.name}" has type "${existing.type}"; drop it before recreating as "${spec.type}"`,
|
|
12399
|
+
errCtx,
|
|
12400
|
+
{ field: "type", reason: "type change requires drop and create" }
|
|
12401
|
+
);
|
|
12402
|
+
}
|
|
12403
|
+
try {
|
|
12404
|
+
await ctx.collection.updateSearchIndex(spec.name, spec.definition);
|
|
12405
|
+
} catch (error) {
|
|
12406
|
+
throw mapSearchIndexError(error, errCtx);
|
|
12407
|
+
}
|
|
12408
|
+
return { name: spec.name, action: "updated", created: false };
|
|
12409
|
+
}
|
|
12410
|
+
try {
|
|
12411
|
+
const name = await ctx.collection.createSearchIndex({ name: spec.name, type: spec.type, definition: spec.definition });
|
|
12412
|
+
return { name: typeof name === "string" && name.length > 0 ? name : spec.name, action: "created", created: true };
|
|
12413
|
+
} catch (error) {
|
|
12414
|
+
const mapped = mapSearchIndexError(error, errCtx);
|
|
12415
|
+
if (mapped.code === ErrorCodes.SEARCH_INDEX_EXISTS && ifExists === "skip") {
|
|
12416
|
+
return { name: spec.name, action: "skipped", created: false };
|
|
12417
|
+
}
|
|
12418
|
+
throw mapped;
|
|
12419
|
+
}
|
|
12420
|
+
}
|
|
12421
|
+
async function createSearchIndexesOp(ctx, specs) {
|
|
12422
|
+
const operation = "createSearchIndexes";
|
|
12423
|
+
if (!Array.isArray(specs) || specs.length === 0) {
|
|
12424
|
+
throw searchIndexError(
|
|
12425
|
+
ErrorCodes.INVALID_SEARCH_INDEX_DEFINITION,
|
|
12426
|
+
`${operation}: specs must be a non-empty array`,
|
|
12427
|
+
{ operation, namespace: ctx.namespace },
|
|
12428
|
+
{ field: "specs", reason: "non-empty array required" }
|
|
12429
|
+
);
|
|
12430
|
+
}
|
|
12431
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12432
|
+
for (const spec of specs) {
|
|
12433
|
+
assertSpec(operation, ctx.namespace, spec);
|
|
12434
|
+
if (seen.has(spec.name)) {
|
|
12435
|
+
throw searchIndexError(
|
|
12436
|
+
ErrorCodes.SEARCH_INDEX_EXISTS,
|
|
12437
|
+
`Search index "${spec.name}" is listed more than once`,
|
|
12438
|
+
{ operation, namespace: ctx.namespace, indexName: spec.name }
|
|
12439
|
+
);
|
|
12440
|
+
}
|
|
12441
|
+
seen.add(spec.name);
|
|
12442
|
+
}
|
|
12443
|
+
try {
|
|
12444
|
+
return await ctx.collection.createSearchIndexes(specs.map(({ name, type, definition }) => ({ name, type, definition })));
|
|
12445
|
+
} catch (error) {
|
|
12446
|
+
throw mapSearchIndexError(error, { operation, namespace: ctx.namespace });
|
|
12447
|
+
}
|
|
12448
|
+
}
|
|
12449
|
+
async function updateSearchIndexOp(ctx, name, definition) {
|
|
12450
|
+
const operation = "updateSearchIndex";
|
|
12451
|
+
assertIndexName(operation, ctx.namespace, name);
|
|
12452
|
+
assertDefinition(operation, ctx.namespace, name, definition);
|
|
12453
|
+
const errCtx = { operation, namespace: ctx.namespace, indexName: name };
|
|
12454
|
+
const existing = await fetchByName(ctx, operation, name);
|
|
12455
|
+
if (existing.status === "DOES_NOT_EXIST") {
|
|
12456
|
+
throw searchIndexError(ErrorCodes.SEARCH_INDEX_NOT_FOUND, `Search index "${name}" was not found`, errCtx);
|
|
12457
|
+
}
|
|
12458
|
+
if (existing.type !== void 0) assertDefinition(operation, ctx.namespace, name, definition, existing.type);
|
|
12459
|
+
try {
|
|
12460
|
+
await ctx.collection.updateSearchIndex(name, definition);
|
|
12461
|
+
} catch (error) {
|
|
12462
|
+
throw mapSearchIndexError(error, errCtx);
|
|
12463
|
+
}
|
|
12464
|
+
}
|
|
12465
|
+
async function dropSearchIndexOp(ctx, name, options) {
|
|
12466
|
+
const operation = "dropSearchIndex";
|
|
12467
|
+
assertIndexName(operation, ctx.namespace, name);
|
|
12468
|
+
assertOptionsObject(operation, ctx.namespace, name, options);
|
|
12469
|
+
assertBooleanOption(operation, ctx.namespace, name, "options.ignoreMissing", options?.ignoreMissing);
|
|
12470
|
+
const errCtx = { operation, namespace: ctx.namespace, indexName: name };
|
|
12471
|
+
const existing = await fetchByName(ctx, operation, name);
|
|
12472
|
+
if (existing.status === "DOES_NOT_EXIST") {
|
|
12473
|
+
if (options?.ignoreMissing) return;
|
|
12474
|
+
throw searchIndexError(ErrorCodes.SEARCH_INDEX_NOT_FOUND, `Search index "${name}" was not found`, errCtx);
|
|
12475
|
+
}
|
|
12476
|
+
try {
|
|
12477
|
+
await ctx.collection.dropSearchIndex(name);
|
|
12478
|
+
} catch (error) {
|
|
12479
|
+
const mapped = mapSearchIndexError(error, errCtx);
|
|
12480
|
+
if (mapped.code === ErrorCodes.SEARCH_INDEX_NOT_FOUND && options?.ignoreMissing) return;
|
|
12481
|
+
throw mapped;
|
|
12482
|
+
}
|
|
12483
|
+
}
|
|
12484
|
+
function sleep2(ms, signal) {
|
|
12485
|
+
return new Promise((resolve, reject) => {
|
|
12486
|
+
if (signal?.aborted) {
|
|
12487
|
+
reject(signal.reason);
|
|
12488
|
+
return;
|
|
12489
|
+
}
|
|
12490
|
+
const onAbort = () => {
|
|
12491
|
+
clearTimeout(timer);
|
|
12492
|
+
reject(signal?.reason);
|
|
12493
|
+
};
|
|
12494
|
+
const timer = setTimeout(() => {
|
|
12495
|
+
signal?.removeEventListener("abort", onAbort);
|
|
12496
|
+
resolve();
|
|
12497
|
+
}, ms);
|
|
12498
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
12499
|
+
});
|
|
12500
|
+
}
|
|
12501
|
+
function linkSignal(userSignal, remainingMs) {
|
|
12502
|
+
const controller = new AbortController();
|
|
12503
|
+
let expired = false;
|
|
12504
|
+
const onUserAbort = () => controller.abort(userSignal?.reason);
|
|
12505
|
+
userSignal?.addEventListener("abort", onUserAbort, { once: true });
|
|
12506
|
+
const timer = setTimeout(() => {
|
|
12507
|
+
expired = true;
|
|
12508
|
+
controller.abort(new Error("search index wait deadline reached"));
|
|
12509
|
+
}, Math.max(0, remainingMs));
|
|
12510
|
+
return {
|
|
12511
|
+
signal: controller.signal,
|
|
12512
|
+
timedOut: () => expired,
|
|
12513
|
+
dispose: () => {
|
|
12514
|
+
clearTimeout(timer);
|
|
12515
|
+
userSignal?.removeEventListener("abort", onUserAbort);
|
|
12516
|
+
}
|
|
12517
|
+
};
|
|
12518
|
+
}
|
|
12519
|
+
async function waitForSearchIndexOp(ctx, name, options, clock = Date.now) {
|
|
12520
|
+
const operation = "waitForSearchIndex";
|
|
12521
|
+
assertIndexName(operation, ctx.namespace, name);
|
|
12522
|
+
const { until, timeoutMs, intervalMs, signal } = resolveWaitOptions(ctx.namespace, name, options);
|
|
12523
|
+
const errCtx = { operation, namespace: ctx.namespace, indexName: name };
|
|
12524
|
+
if (signal?.aborted) throw signal.reason;
|
|
12525
|
+
const start = clock();
|
|
12526
|
+
const grace = Math.min(SEARCH_INDEX_MISSING_GRACE_MS, timeoutMs);
|
|
12527
|
+
let last;
|
|
12528
|
+
const timeoutError = () => searchIndexError(
|
|
12529
|
+
ErrorCodes.SEARCH_INDEX_WAIT_TIMEOUT,
|
|
12530
|
+
`Timed out after ${timeoutMs}ms waiting for Search index "${name}" to become ${until}`,
|
|
12531
|
+
errCtx,
|
|
12532
|
+
{ until, timeoutMs, last }
|
|
12533
|
+
);
|
|
12534
|
+
for (; ; ) {
|
|
12535
|
+
const link = linkSignal(signal, timeoutMs - (clock() - start));
|
|
12536
|
+
let info;
|
|
12537
|
+
try {
|
|
12538
|
+
info = await fetchByName(ctx, operation, name, link.signal);
|
|
12539
|
+
} catch (error) {
|
|
12540
|
+
if (signal?.aborted) throw signal.reason;
|
|
12541
|
+
if (link.timedOut()) throw timeoutError();
|
|
12542
|
+
throw error;
|
|
12543
|
+
} finally {
|
|
12544
|
+
link.dispose();
|
|
12545
|
+
}
|
|
12546
|
+
last = info;
|
|
12547
|
+
if (signal?.aborted) throw signal.reason;
|
|
12548
|
+
if (until === "dropped") {
|
|
12549
|
+
if (info.status === "DOES_NOT_EXIST") return info;
|
|
12550
|
+
} else if (info.status === "DOES_NOT_EXIST") {
|
|
12551
|
+
if (clock() - start >= grace) {
|
|
12552
|
+
throw searchIndexError(
|
|
12553
|
+
ErrorCodes.SEARCH_INDEX_NOT_FOUND,
|
|
12554
|
+
`Search index "${name}" was not found within ${grace}ms`,
|
|
12555
|
+
errCtx,
|
|
12556
|
+
{ until, graceMs: grace }
|
|
12557
|
+
);
|
|
12558
|
+
}
|
|
12559
|
+
} else if (until === "queryable") {
|
|
12560
|
+
if (info.queryable) return info;
|
|
12561
|
+
if (info.status === "FAILED") {
|
|
12562
|
+
throw searchIndexError(
|
|
12563
|
+
ErrorCodes.SEARCH_INDEX_BUILD_FAILED,
|
|
12564
|
+
`Search index "${name}" build failed`,
|
|
12565
|
+
errCtx,
|
|
12566
|
+
{ status: info.status, statusDetail: info.statusDetail }
|
|
12567
|
+
);
|
|
12568
|
+
}
|
|
12569
|
+
} else {
|
|
12570
|
+
if (info.status === "READY") return info;
|
|
12571
|
+
if (info.status === "FAILED") {
|
|
12572
|
+
throw searchIndexError(
|
|
12573
|
+
ErrorCodes.SEARCH_INDEX_BUILD_FAILED,
|
|
12574
|
+
`Search index "${name}" build failed`,
|
|
12575
|
+
errCtx,
|
|
12576
|
+
{ status: info.status, statusDetail: info.statusDetail }
|
|
12577
|
+
);
|
|
12578
|
+
}
|
|
12579
|
+
}
|
|
12580
|
+
const remaining = timeoutMs - (clock() - start);
|
|
12581
|
+
if (remaining <= 0) throw timeoutError();
|
|
12582
|
+
await sleep2(Math.min(intervalMs, remaining), signal);
|
|
12583
|
+
}
|
|
12584
|
+
}
|
|
12585
|
+
|
|
12121
12586
|
// src/adapters/mongodb/common/collection-accessor-cache-helpers.ts
|
|
12122
12587
|
var import_node_util4 = require("node:util");
|
|
12123
12588
|
function isRecord2(value) {
|
|
@@ -12478,7 +12943,7 @@ async function invalidateReadCachesAfterWrite(queryCache, collection, dbName, co
|
|
|
12478
12943
|
}
|
|
12479
12944
|
|
|
12480
12945
|
// src/adapters/mongodb/writes/write-utils.ts
|
|
12481
|
-
function
|
|
12946
|
+
function sleep3(ms) {
|
|
12482
12947
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12483
12948
|
}
|
|
12484
12949
|
function splitIntoBatches(items, batchSize) {
|
|
@@ -12898,7 +13363,7 @@ async function insertBatchDocuments(collection, documents, options = {}) {
|
|
|
12898
13363
|
result.retries.push(retryInfo);
|
|
12899
13364
|
onRetry?.(retryInfo);
|
|
12900
13365
|
if (retryDelay > 0) {
|
|
12901
|
-
await
|
|
13366
|
+
await sleep3(retryDelay);
|
|
12902
13367
|
}
|
|
12903
13368
|
continue;
|
|
12904
13369
|
}
|
|
@@ -13027,7 +13492,7 @@ async function updateBatchDocuments(collection, filter, update, options = {}) {
|
|
|
13027
13492
|
result.retries.push(retryInfo);
|
|
13028
13493
|
onRetry?.(retryInfo);
|
|
13029
13494
|
if (retryDelay > 0) {
|
|
13030
|
-
await
|
|
13495
|
+
await sleep3(retryDelay);
|
|
13031
13496
|
}
|
|
13032
13497
|
continue;
|
|
13033
13498
|
}
|
|
@@ -13122,7 +13587,7 @@ async function deleteBatchDocuments(collection, filter, options = {}) {
|
|
|
13122
13587
|
result.retries.push(retryInfo);
|
|
13123
13588
|
onRetry?.(retryInfo);
|
|
13124
13589
|
if (retryDelay > 0) {
|
|
13125
|
-
await
|
|
13590
|
+
await sleep3(retryDelay);
|
|
13126
13591
|
}
|
|
13127
13592
|
continue;
|
|
13128
13593
|
}
|
|
@@ -13619,7 +14084,7 @@ var MongoCollectionAccessor = class {
|
|
|
13619
14084
|
const { query: normalizedQuery, options: driverOptions } = snapshotReadInputs(this._cvFilter(query), normalizeFindProjectionOptions(rawOptions), this.collectionRef.bsonOptions);
|
|
13620
14085
|
const cacheTTL = rawOptions.explain ? 0 : typeof rawOptions.cache === "number" ? rawOptions.cache : 0;
|
|
13621
14086
|
if (cacheTTL > 0 && this.management.queryCache && canCacheBsonResult(driverOptions) && !hasSessionOption(driverOptions) && !await isCollectionCacheBarrierActive(this.management.queryCache, this.collectionRef, this.management.defaults)) {
|
|
13622
|
-
const cacheKey = buildFindOneCacheKey(this.collectionRef, this.management.defaults, normalizedQuery, driverOptions);
|
|
14087
|
+
const cacheKey = buildFindOneCacheKey(this.collectionRef, this.management.defaults, normalizedQuery, driverOptions, true);
|
|
13623
14088
|
const result2 = await readThroughCache(this.management.queryCache, cacheKey, cacheTTL, () => findOneDocument(this.collectionRef, normalizedQuery, driverOptions));
|
|
13624
14089
|
return wrapQueryResultWithMeta(this.collectionRef, this.management.defaults, "findOne", rawOptions, startTs, result2);
|
|
13625
14090
|
}
|
|
@@ -13676,7 +14141,7 @@ var MongoCollectionAccessor = class {
|
|
|
13676
14141
|
);
|
|
13677
14142
|
const countQueue = this.management.defaults?.countQueue;
|
|
13678
14143
|
if (cacheTTL > 0 && this.management.queryCache && canCacheBsonCount(keyOptions) && !hasSessionOption(keyOptions) && !await isCollectionCacheBarrierActive(this.management.queryCache, this.collectionRef, this.management.defaults)) {
|
|
13679
|
-
const cacheKey = buildCountCacheKey(this.collectionRef, this.management.defaults, normalizedQuery, keyOptions);
|
|
14144
|
+
const cacheKey = buildCountCacheKey(this.collectionRef, this.management.defaults, normalizedQuery, keyOptions, true);
|
|
13680
14145
|
const result = await readThroughCache(
|
|
13681
14146
|
this.management.queryCache,
|
|
13682
14147
|
cacheKey,
|
|
@@ -13769,7 +14234,7 @@ var MongoCollectionAccessor = class {
|
|
|
13769
14234
|
driverOptions
|
|
13770
14235
|
);
|
|
13771
14236
|
if (cacheTTL > 0 && this.management.queryCache && canCacheBsonResult(driverOptions) && !hasSessionOption(driverOptions) && !await isCollectionCacheBarrierActive(this.management.queryCache, this.collectionRef, this.management.defaults)) {
|
|
13772
|
-
const cacheKey = buildDistinctCacheKey(this.collectionRef, this.management.defaults, key, normalizedQuery, driverOptions);
|
|
14237
|
+
const cacheKey = buildDistinctCacheKey(this.collectionRef, this.management.defaults, key, normalizedQuery, driverOptions, true);
|
|
13773
14238
|
const result2 = await readThroughCache(this.management.queryCache, cacheKey, cacheTTL, executeDistinct);
|
|
13774
14239
|
return wrapQueryResultWithMeta(this.collectionRef, this.management.defaults, "distinct", rawOptions, startTs, result2);
|
|
13775
14240
|
}
|
|
@@ -13890,6 +14355,35 @@ var MongoCollectionAccessor = class {
|
|
|
13890
14355
|
this.assertWritePath("dropIndexes", "management");
|
|
13891
14356
|
return dropIndexesForAccessor(this.collectionRef);
|
|
13892
14357
|
}
|
|
14358
|
+
/** Search index lifecycle (v3.4.0); writes are governed by writePathPolicy `management`. */
|
|
14359
|
+
searchIndexContext() {
|
|
14360
|
+
return { collection: this.collectionRef, namespace: this.getNamespace() };
|
|
14361
|
+
}
|
|
14362
|
+
async createSearchIndex(spec, options) {
|
|
14363
|
+
this.assertWritePath("createSearchIndex", "management");
|
|
14364
|
+
return createSearchIndexOp(this.searchIndexContext(), spec, options);
|
|
14365
|
+
}
|
|
14366
|
+
async createSearchIndexes(specs) {
|
|
14367
|
+
this.assertWritePath("createSearchIndexes", "management");
|
|
14368
|
+
return createSearchIndexesOp(this.searchIndexContext(), specs);
|
|
14369
|
+
}
|
|
14370
|
+
async listSearchIndexes(options) {
|
|
14371
|
+
return listSearchIndexesOp(this.searchIndexContext(), options);
|
|
14372
|
+
}
|
|
14373
|
+
async getSearchIndex(name) {
|
|
14374
|
+
return getSearchIndexOp(this.searchIndexContext(), name);
|
|
14375
|
+
}
|
|
14376
|
+
async updateSearchIndex(name, definition) {
|
|
14377
|
+
this.assertWritePath("updateSearchIndex", "management");
|
|
14378
|
+
return updateSearchIndexOp(this.searchIndexContext(), name, definition);
|
|
14379
|
+
}
|
|
14380
|
+
async dropSearchIndex(name, options) {
|
|
14381
|
+
this.assertWritePath("dropSearchIndex", "management");
|
|
14382
|
+
return dropSearchIndexOp(this.searchIndexContext(), name, options);
|
|
14383
|
+
}
|
|
14384
|
+
async waitForSearchIndex(name, options) {
|
|
14385
|
+
return waitForSearchIndexOp(this.searchIndexContext(), name, options);
|
|
14386
|
+
}
|
|
13893
14387
|
/** Pre-populates bookmark cache entries for the specified key dimensions and page numbers. */
|
|
13894
14388
|
async prewarmBookmarks(keyDims = {}, pages = []) {
|
|
13895
14389
|
return prewarmBookmarksForAccessor(this.bookmarkContext(), keyDims, pages);
|
|
@@ -14774,7 +15268,7 @@ var LockManager = class {
|
|
|
14774
15268
|
break;
|
|
14775
15269
|
}
|
|
14776
15270
|
const delay3 = retryDelay * Math.pow(retryBackoff, attempt);
|
|
14777
|
-
await
|
|
15271
|
+
await sleep4(delay3);
|
|
14778
15272
|
}
|
|
14779
15273
|
this.stats.errors += 1;
|
|
14780
15274
|
if (options.fallbackToNoLock) {
|
|
@@ -14886,7 +15380,7 @@ var LockManager = class {
|
|
|
14886
15380
|
}
|
|
14887
15381
|
}
|
|
14888
15382
|
};
|
|
14889
|
-
async function
|
|
15383
|
+
async function sleep4(ms) {
|
|
14890
15384
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
14891
15385
|
}
|
|
14892
15386
|
var DistributedCacheLockManager = class {
|
|
@@ -15011,10 +15505,10 @@ var DistributedCacheLockManager = class {
|
|
|
15011
15505
|
return new Lock(key, lockId, this, ttl, startedAt);
|
|
15012
15506
|
}
|
|
15013
15507
|
if (attempt === retryTimes) break;
|
|
15014
|
-
await
|
|
15508
|
+
await sleep4(retryDelay);
|
|
15015
15509
|
} catch (error) {
|
|
15016
15510
|
if (attempt === retryTimes) throw error;
|
|
15017
|
-
await
|
|
15511
|
+
await sleep4(retryDelay);
|
|
15018
15512
|
}
|
|
15019
15513
|
}
|
|
15020
15514
|
this.stats.errors++;
|
|
@@ -18040,7 +18534,7 @@ var DEFAULT_DATA_TASK_MAX_DOCUMENTS = 1e4;
|
|
|
18040
18534
|
var DEFAULT_DATA_TASK_BACKUP_MAX_BYTES = 256 * 1024 * 1024;
|
|
18041
18535
|
var ENVIRONMENTS = /* @__PURE__ */ new Set(["development", "test", "staging", "production", "prod", "live"]);
|
|
18042
18536
|
var PRODUCTION_ENVIRONMENTS = /* @__PURE__ */ new Set(["production", "prod", "live"]);
|
|
18043
|
-
function
|
|
18537
|
+
function invalid2(message, collection) {
|
|
18044
18538
|
throw new DataTaskJobError("INVALID_JOB", message, "validate", collection);
|
|
18045
18539
|
}
|
|
18046
18540
|
function canonicalize(value) {
|
|
@@ -18078,15 +18572,15 @@ function connectionDescriptor(value) {
|
|
|
18078
18572
|
}
|
|
18079
18573
|
function validateConnection(value, field) {
|
|
18080
18574
|
if (isDataTaskJobRuntime(value)) return;
|
|
18081
|
-
if (!isRecord3(value) || value.type !== "mongodb")
|
|
18082
|
-
if (typeof value.databaseName !== "string" || value.databaseName.trim() === "")
|
|
18083
|
-
if (!isRecord3(value.config) || typeof value.config.uri !== "string" || value.config.uri.trim() === "")
|
|
18575
|
+
if (!isRecord3(value) || value.type !== "mongodb") invalid2(`${field} must be a monSQLize runtime or MongoDB MonSQLizeOptions.`);
|
|
18576
|
+
if (typeof value.databaseName !== "string" || value.databaseName.trim() === "") invalid2(`${field}.databaseName is required.`);
|
|
18577
|
+
if (!isRecord3(value.config) || typeof value.config.uri !== "string" || value.config.uri.trim() === "") invalid2(`${field}.config.uri is required.`);
|
|
18084
18578
|
}
|
|
18085
18579
|
function uniqueFields(value, pathName, collection) {
|
|
18086
|
-
if (!Array.isArray(value) || value.length === 0)
|
|
18580
|
+
if (!Array.isArray(value) || value.length === 0) invalid2(`${pathName} must be a non-empty string array.`, collection);
|
|
18087
18581
|
const fields = value.map((item) => typeof item === "string" ? item.trim() : "");
|
|
18088
|
-
if (fields.some((item) => item === ""))
|
|
18089
|
-
if (new Set(fields).size !== fields.length)
|
|
18582
|
+
if (fields.some((item) => item === "")) invalid2(`${pathName} must contain non-empty strings.`, collection);
|
|
18583
|
+
if (new Set(fields).size !== fields.length) invalid2(`${pathName} must not contain duplicates.`, collection);
|
|
18090
18584
|
return fields;
|
|
18091
18585
|
}
|
|
18092
18586
|
var FORBIDDEN_FIELD_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
@@ -18097,31 +18591,31 @@ function assertBsonLiteral(value, pathName, collection, stack = /* @__PURE__ */
|
|
|
18097
18591
|
const valueType = typeof value;
|
|
18098
18592
|
if (value === null || BSON_PRIMITIVE_TYPES.has(valueType)) return;
|
|
18099
18593
|
if (BSON_UNSUPPORTED_TYPES.has(valueType)) {
|
|
18100
|
-
|
|
18594
|
+
invalid2(`${pathName} must contain only BSON-compatible literals.`, collection);
|
|
18101
18595
|
}
|
|
18102
18596
|
if (value instanceof Date || value instanceof RegExp || Buffer.isBuffer(value)) return;
|
|
18103
18597
|
if (Array.isArray(value)) {
|
|
18104
|
-
if (stack.has(value))
|
|
18598
|
+
if (stack.has(value)) invalid2(`${pathName} must not contain circular values.`, collection);
|
|
18105
18599
|
stack.add(value);
|
|
18106
18600
|
value.forEach((item, position) => assertBsonLiteral(item, `${pathName}[${position}]`, collection, stack));
|
|
18107
18601
|
stack.delete(value);
|
|
18108
18602
|
return;
|
|
18109
18603
|
}
|
|
18110
|
-
if (!isRecord3(value))
|
|
18604
|
+
if (!isRecord3(value)) invalid2(`${pathName} must contain only BSON-compatible literals.`, collection);
|
|
18111
18605
|
if (typeof value._bsontype === "string") return;
|
|
18112
18606
|
const prototype = Object.getPrototypeOf(value);
|
|
18113
|
-
if (!PLAIN_OBJECT_PROTOTYPES.has(prototype))
|
|
18114
|
-
if (stack.has(value))
|
|
18607
|
+
if (!PLAIN_OBJECT_PROTOTYPES.has(prototype)) invalid2(`${pathName} must contain only BSON-compatible literals.`, collection);
|
|
18608
|
+
if (stack.has(value)) invalid2(`${pathName} must not contain circular values.`, collection);
|
|
18115
18609
|
stack.add(value);
|
|
18116
18610
|
for (const [key, item] of Object.entries(value)) assertBsonLiteral(item, `${pathName}.${key}`, collection, stack);
|
|
18117
18611
|
stack.delete(value);
|
|
18118
18612
|
}
|
|
18119
18613
|
function normalizeFieldPath(value, pathName, collection) {
|
|
18120
|
-
if (typeof value !== "string" || value.trim() === "")
|
|
18614
|
+
if (typeof value !== "string" || value.trim() === "") invalid2(`${pathName} must be a non-empty field path.`, collection);
|
|
18121
18615
|
const field = value.trim();
|
|
18122
18616
|
const segments = field.split(".");
|
|
18123
18617
|
if (segments.some((segment) => segment === "" || segment.startsWith("$") || FORBIDDEN_FIELD_PATH_SEGMENTS.has(segment))) {
|
|
18124
|
-
|
|
18618
|
+
invalid2(`${pathName} contains an unsafe field path.`, collection);
|
|
18125
18619
|
}
|
|
18126
18620
|
return field;
|
|
18127
18621
|
}
|
|
@@ -18137,13 +18631,13 @@ function projectionIncludes(projection, field) {
|
|
|
18137
18631
|
return included.some((key) => key === field || field.startsWith(`${key}.`));
|
|
18138
18632
|
}
|
|
18139
18633
|
function normalizeFieldPatch(data, identityFields, collection) {
|
|
18140
|
-
if (data.rename !== void 0 && !isRecord3(data.rename))
|
|
18141
|
-
if (data.set !== void 0 && !isRecord3(data.set))
|
|
18142
|
-
if (data.unset !== void 0 && !Array.isArray(data.unset))
|
|
18634
|
+
if (data.rename !== void 0 && !isRecord3(data.rename)) invalid2("data.rename must be an object.", collection);
|
|
18635
|
+
if (data.set !== void 0 && !isRecord3(data.set)) invalid2("data.set must be an object.", collection);
|
|
18636
|
+
if (data.unset !== void 0 && !Array.isArray(data.unset)) invalid2("data.unset must be an array.", collection);
|
|
18143
18637
|
const renameEntries = Object.entries(data.rename ?? {}).map(([source, destination], position) => {
|
|
18144
18638
|
const normalizedSource = normalizeFieldPath(source, `data.rename source ${position}`, collection);
|
|
18145
18639
|
const normalizedDestination = normalizeFieldPath(destination, `data.rename.${source}`, collection);
|
|
18146
|
-
if (pathsOverlap(normalizedSource, normalizedDestination))
|
|
18640
|
+
if (pathsOverlap(normalizedSource, normalizedDestination)) invalid2("data.rename source and destination must not overlap.", collection);
|
|
18147
18641
|
return [normalizedSource, normalizedDestination];
|
|
18148
18642
|
});
|
|
18149
18643
|
const setEntries = Object.entries(data.set ?? {}).map(([field, value], position) => {
|
|
@@ -18152,14 +18646,14 @@ function normalizeFieldPatch(data, identityFields, collection) {
|
|
|
18152
18646
|
try {
|
|
18153
18647
|
canonicalStringify(value);
|
|
18154
18648
|
} catch {
|
|
18155
|
-
|
|
18649
|
+
invalid2(`data.set.${field} must be a BSON-compatible literal.`, collection);
|
|
18156
18650
|
}
|
|
18157
18651
|
return [normalizedField, value];
|
|
18158
18652
|
});
|
|
18159
18653
|
const unset = (data.unset ?? []).map((field, position) => normalizeFieldPath(field, `data.unset[${position}]`, collection));
|
|
18160
|
-
if (new Set(unset).size !== unset.length)
|
|
18654
|
+
if (new Set(unset).size !== unset.length) invalid2("data.unset must not contain duplicates.", collection);
|
|
18161
18655
|
const destinations = renameEntries.map(([, destination]) => destination);
|
|
18162
|
-
if (new Set(destinations).size !== destinations.length)
|
|
18656
|
+
if (new Set(destinations).size !== destinations.length) invalid2("data.rename destinations must be unique.", collection);
|
|
18163
18657
|
const configuredPaths = [
|
|
18164
18658
|
...renameEntries.flatMap(([source, destination]) => [
|
|
18165
18659
|
{ path: source, label: `rename source "${source}"` },
|
|
@@ -18171,18 +18665,18 @@ function normalizeFieldPatch(data, identityFields, collection) {
|
|
|
18171
18665
|
for (let left = 0; left < configuredPaths.length; left += 1) {
|
|
18172
18666
|
for (let right = left + 1; right < configuredPaths.length; right += 1) {
|
|
18173
18667
|
if (pathsOverlap(configuredPaths[left].path, configuredPaths[right].path)) {
|
|
18174
|
-
|
|
18668
|
+
invalid2(`${configuredPaths[left].label} conflicts with ${configuredPaths[right].label}.`, collection);
|
|
18175
18669
|
}
|
|
18176
18670
|
}
|
|
18177
18671
|
}
|
|
18178
18672
|
for (const configured of configuredPaths) {
|
|
18179
18673
|
if (configured.path === "_id" || configured.path.startsWith("_id.") || identityFields.some((identity) => pathsOverlap(configured.path, identity))) {
|
|
18180
|
-
|
|
18674
|
+
invalid2(`${configured.label} must not overlap _id or an identity field.`, collection);
|
|
18181
18675
|
}
|
|
18182
18676
|
}
|
|
18183
18677
|
if (data.projection) {
|
|
18184
18678
|
for (const required of [...identityFields, ...renameEntries.map(([source]) => source)]) {
|
|
18185
|
-
if (!projectionIncludes(data.projection, required))
|
|
18679
|
+
if (!projectionIncludes(data.projection, required)) invalid2(`data.projection must include required field "${required}".`, collection);
|
|
18186
18680
|
}
|
|
18187
18681
|
}
|
|
18188
18682
|
return {
|
|
@@ -18193,105 +18687,105 @@ function normalizeFieldPatch(data, identityFields, collection) {
|
|
|
18193
18687
|
}
|
|
18194
18688
|
function normalizeData(data, collection) {
|
|
18195
18689
|
if (data === void 0) return void 0;
|
|
18196
|
-
if (!isRecord3(data))
|
|
18690
|
+
if (!isRecord3(data)) invalid2("data must be an object.", collection);
|
|
18197
18691
|
const hasFilter = isRecord3(data.filter) && Object.keys(data.filter).length > 0;
|
|
18198
|
-
if (hasFilter === (data.all === true))
|
|
18692
|
+
if (hasFilter === (data.all === true)) invalid2("data requires exactly one of a non-empty filter or all: true.", collection);
|
|
18199
18693
|
if (!isRecord3(data.identity) || data.identity.mode !== "fields" && data.identity.mode !== "source-id") {
|
|
18200
|
-
|
|
18694
|
+
invalid2("data.identity.mode must be fields or source-id.", collection);
|
|
18201
18695
|
}
|
|
18202
18696
|
const identity = data.identity.mode === "fields" ? { mode: "fields", fields: uniqueFields(data.identity.fields, "identity.fields", collection) } : {
|
|
18203
18697
|
mode: "source-id",
|
|
18204
18698
|
...data.identity.conflictBy === void 0 ? {} : { conflictBy: uniqueFields(data.identity.conflictBy, "identity.conflictBy", collection) }
|
|
18205
18699
|
};
|
|
18206
|
-
if (data.projection !== void 0 && !isRecord3(data.projection))
|
|
18700
|
+
if (data.projection !== void 0 && !isRecord3(data.projection)) invalid2("data.projection must be an object.", collection);
|
|
18207
18701
|
if (data.projection) assertBsonLiteral(data.projection, "data.projection", collection);
|
|
18208
18702
|
if (hasFilter) assertBsonLiteral(data.filter, "data.filter", collection);
|
|
18209
18703
|
const identityFields = identity.mode === "fields" ? identity.fields : ["_id", ...identity.conflictBy ?? []];
|
|
18210
18704
|
const patch = normalizeFieldPatch(data, identityFields, collection);
|
|
18211
18705
|
const batchSize = data.batchSize ?? 500;
|
|
18212
|
-
if (!Number.isInteger(batchSize) || batchSize < 1 || batchSize > 1e4)
|
|
18706
|
+
if (!Number.isInteger(batchSize) || batchSize < 1 || batchSize > 1e4) invalid2("data.batchSize must be an integer from 1 to 10000.", collection);
|
|
18213
18707
|
const maxDocuments = data.maxDocuments ?? DEFAULT_DATA_TASK_MAX_DOCUMENTS;
|
|
18214
18708
|
if (!Number.isInteger(maxDocuments) || maxDocuments < 1 || maxDocuments > 1e6) {
|
|
18215
|
-
|
|
18709
|
+
invalid2("data.maxDocuments must be an integer from 1 to 1000000.", collection);
|
|
18216
18710
|
}
|
|
18217
18711
|
const strategy = data.strategy ?? "upsert";
|
|
18218
|
-
if (strategy !== "upsert" && strategy !== "insert")
|
|
18712
|
+
if (strategy !== "upsert" && strategy !== "insert") invalid2("data.strategy must be upsert or insert.", collection);
|
|
18219
18713
|
return { ...data, ...patch, filter: hasFilter ? data.filter : {}, all: data.all === true, identity, strategy, batchSize, maxDocuments };
|
|
18220
18714
|
}
|
|
18221
18715
|
function normalizeIndexes(indexes, collection) {
|
|
18222
18716
|
if (indexes === void 0) return [];
|
|
18223
|
-
if (!Array.isArray(indexes))
|
|
18717
|
+
if (!Array.isArray(indexes)) invalid2("indexes must be an array.", collection);
|
|
18224
18718
|
const names = /* @__PURE__ */ new Set();
|
|
18225
18719
|
return indexes.map((index, position) => {
|
|
18226
|
-
if (!isRecord3(index) || !isRecord3(index.key) || Object.keys(index.key).length === 0)
|
|
18720
|
+
if (!isRecord3(index) || !isRecord3(index.key) || Object.keys(index.key).length === 0) invalid2(`indexes[${position}].key must be non-empty.`, collection);
|
|
18227
18721
|
assertBsonLiteral(index.key, `indexes[${position}].key`, collection);
|
|
18228
|
-
if (index.name !== void 0 && (typeof index.name !== "string" || index.name.trim() === ""))
|
|
18229
|
-
if (index.name && names.has(index.name))
|
|
18722
|
+
if (index.name !== void 0 && (typeof index.name !== "string" || index.name.trim() === "")) invalid2(`indexes[${position}].name must be non-empty.`, collection);
|
|
18723
|
+
if (index.name && names.has(index.name)) invalid2(`duplicate index name "${index.name}".`, collection);
|
|
18230
18724
|
if (index.name) names.add(index.name);
|
|
18231
|
-
if (index.options !== void 0 && !isRecord3(index.options))
|
|
18725
|
+
if (index.options !== void 0 && !isRecord3(index.options)) invalid2(`indexes[${position}].options must be an object.`, collection);
|
|
18232
18726
|
if (index.options) assertBsonLiteral(index.options, `indexes[${position}].options`, collection);
|
|
18233
|
-
if (index.options && "name" in index.options)
|
|
18234
|
-
if (Object.keys(index.key).length === 1 && index.key._id === 1 || index.name === "_id_")
|
|
18727
|
+
if (index.options && "name" in index.options) invalid2(`indexes[${position}].options.name is not allowed; use indexes[${position}].name.`, collection);
|
|
18728
|
+
if (Object.keys(index.key).length === 1 && index.key._id === 1 || index.name === "_id_") invalid2("indexes must not declare the built-in _id index.", collection);
|
|
18235
18729
|
return { key: { ...index.key }, ...index.name ? { name: index.name } : {}, options: { ...index.options ?? {} } };
|
|
18236
18730
|
});
|
|
18237
18731
|
}
|
|
18238
18732
|
function normalizeVerify(verify, collection) {
|
|
18239
|
-
if (verify !== void 0 && !isRecord3(verify))
|
|
18733
|
+
if (verify !== void 0 && !isRecord3(verify)) invalid2("verify must be an object.", collection);
|
|
18240
18734
|
const input = verify;
|
|
18241
18735
|
const mode = input?.mode ?? "sample";
|
|
18242
|
-
if (mode !== "sample" && mode !== "full")
|
|
18736
|
+
if (mode !== "sample" && mode !== "full") invalid2("verify.mode must be sample or full.", collection);
|
|
18243
18737
|
const sampleSize = input?.sampleSize ?? 20;
|
|
18244
|
-
if (!Number.isInteger(sampleSize) || sampleSize < 1 || sampleSize > 1e3)
|
|
18738
|
+
if (!Number.isInteger(sampleSize) || sampleSize < 1 || sampleSize > 1e3) invalid2("verify.sampleSize must be an integer from 1 to 1000.", collection);
|
|
18245
18739
|
const fields = input?.fields === void 0 ? void 0 : uniqueFields(input.fields, "verify.fields", collection);
|
|
18246
18740
|
return { mode, sampleSize, ...fields ? { fields } : {} };
|
|
18247
18741
|
}
|
|
18248
18742
|
function normalizeBackup(backup, production) {
|
|
18249
|
-
if (backup !== void 0 && !isRecord3(backup))
|
|
18743
|
+
if (backup !== void 0 && !isRecord3(backup)) invalid2("backup must be an object.");
|
|
18250
18744
|
const input = backup;
|
|
18251
|
-
if (production && (typeof input?.dir !== "string" || input.dir.trim() === ""))
|
|
18745
|
+
if (production && (typeof input?.dir !== "string" || input.dir.trim() === "")) invalid2("production jobs require an explicit durable backup.dir.");
|
|
18252
18746
|
const dir = typeof input?.dir === "string" && input.dir.trim() ? input.dir : import_node_path3.default.join(import_node_os2.default.tmpdir(), "monsqlize-data-tasks");
|
|
18253
18747
|
const compression = input?.compression ?? "gzip";
|
|
18254
|
-
if (compression !== "gzip" && compression !== "none")
|
|
18255
|
-
if (input?.format !== void 0 && input.format !== "extended-jsonl")
|
|
18748
|
+
if (compression !== "gzip" && compression !== "none") invalid2("backup.compression must be gzip or none.");
|
|
18749
|
+
if (input?.format !== void 0 && input.format !== "extended-jsonl") invalid2("backup.format must be extended-jsonl.");
|
|
18256
18750
|
const retentionDays = input?.retentionDays ?? 7;
|
|
18257
|
-
if (!Number.isInteger(retentionDays) || retentionDays < 1)
|
|
18751
|
+
if (!Number.isInteger(retentionDays) || retentionDays < 1) invalid2("backup.retentionDays must be a positive integer.");
|
|
18258
18752
|
const maxBytes = input?.maxBytes ?? DEFAULT_DATA_TASK_BACKUP_MAX_BYTES;
|
|
18259
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1)
|
|
18753
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) invalid2("backup.maxBytes must be a positive safe integer.");
|
|
18260
18754
|
return { dir, format: "extended-jsonl", compression, retentionDays, maxBytes };
|
|
18261
18755
|
}
|
|
18262
18756
|
function normalizeLock(lock) {
|
|
18263
18757
|
if (!lock) return false;
|
|
18264
|
-
if (lock !== true && !isRecord3(lock))
|
|
18758
|
+
if (lock !== true && !isRecord3(lock)) invalid2("lock must be a boolean or lease options object.");
|
|
18265
18759
|
const options = lock === true ? {} : lock;
|
|
18266
18760
|
const ttlMs = options.ttlMs ?? 12e4;
|
|
18267
18761
|
const waitTimeoutMs = options.waitTimeoutMs ?? 0;
|
|
18268
|
-
if (!Number.isInteger(ttlMs) || ttlMs < 1e3)
|
|
18269
|
-
if (!Number.isInteger(waitTimeoutMs) || waitTimeoutMs < 0)
|
|
18762
|
+
if (!Number.isInteger(ttlMs) || ttlMs < 1e3) invalid2("lock.ttlMs must be an integer of at least 1000.");
|
|
18763
|
+
if (!Number.isInteger(waitTimeoutMs) || waitTimeoutMs < 0) invalid2("lock.waitTimeoutMs must be a non-negative integer.");
|
|
18270
18764
|
return { ttlMs, waitTimeoutMs };
|
|
18271
18765
|
}
|
|
18272
18766
|
function normalizeDataTaskJob(job) {
|
|
18273
|
-
if (!isRecord3(job))
|
|
18274
|
-
if (typeof job.name !== "string" || job.name.trim() === "")
|
|
18767
|
+
if (!isRecord3(job)) invalid2("job must be an object.");
|
|
18768
|
+
if (typeof job.name !== "string" || job.name.trim() === "") invalid2("job.name is required.");
|
|
18275
18769
|
validateConnection(job.source, "source");
|
|
18276
18770
|
validateConnection(job.target, "target");
|
|
18277
|
-
if (job.source === job.target)
|
|
18771
|
+
if (job.source === job.target) invalid2("source and target must be independent runtimes or connection options.");
|
|
18278
18772
|
const sourceOptions = connectionOptions(job.source);
|
|
18279
18773
|
const targetOptions = connectionOptions(job.target);
|
|
18280
|
-
if (sourceOptions && targetOptions && sourceOptions.databaseName === targetOptions.databaseName && sourceOptions.config?.uri === targetOptions.config?.uri)
|
|
18774
|
+
if (sourceOptions && targetOptions && sourceOptions.databaseName === targetOptions.databaseName && sourceOptions.config?.uri === targetOptions.config?.uri) invalid2("source and target resolve to the same MongoDB database.");
|
|
18281
18775
|
if (typeof job.targetEnvironment !== "string" || !ENVIRONMENTS.has(job.targetEnvironment.toLowerCase())) {
|
|
18282
|
-
|
|
18776
|
+
invalid2("targetEnvironment must be development, test, staging, production, prod, or live.");
|
|
18283
18777
|
}
|
|
18284
|
-
if (!Array.isArray(job.collections) || job.collections.length === 0)
|
|
18778
|
+
if (!Array.isArray(job.collections) || job.collections.length === 0) invalid2("collections must be a non-empty array.");
|
|
18285
18779
|
const targetNames = /* @__PURE__ */ new Set();
|
|
18286
18780
|
const collections = job.collections.map((collection, position) => {
|
|
18287
|
-
if (!isRecord3(collection) || typeof collection.name !== "string" || collection.name.trim() === "")
|
|
18781
|
+
if (!isRecord3(collection) || typeof collection.name !== "string" || collection.name.trim() === "") invalid2(`collections[${position}].name is required.`);
|
|
18288
18782
|
const name = collection.name.trim();
|
|
18289
18783
|
const targetName = typeof collection.targetName === "string" && collection.targetName.trim() ? collection.targetName.trim() : name;
|
|
18290
|
-
if (targetNames.has(targetName))
|
|
18784
|
+
if (targetNames.has(targetName)) invalid2(`target collection "${targetName}" is configured more than once.`, name);
|
|
18291
18785
|
targetNames.add(targetName);
|
|
18292
18786
|
const indexes = normalizeIndexes(collection.indexes, name);
|
|
18293
18787
|
const data = normalizeData(collection.data, name);
|
|
18294
|
-
if (indexes.length === 0 && !data)
|
|
18788
|
+
if (indexes.length === 0 && !data) invalid2("each collection requires indexes or data.", name);
|
|
18295
18789
|
return { name, targetName, indexes, ...data ? { data } : {}, verify: normalizeVerify(collection.verify, name) };
|
|
18296
18790
|
});
|
|
18297
18791
|
const production = PRODUCTION_ENVIRONMENTS.has(job.targetEnvironment.toLowerCase());
|