@atscript/db-mongo 0.1.163 → 0.1.164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +42 -29
- package/dist/index.d.cts +7 -9
- package/dist/index.d.mts +7 -9
- package/dist/index.mjs +42 -29
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2212,6 +2212,37 @@ const validateMongoIdPlugin = (ctx, def, value) => {
|
|
|
2212
2212
|
}
|
|
2213
2213
|
};
|
|
2214
2214
|
//#endregion
|
|
2215
|
+
//#region src/lib/mongo-transaction-support.ts
|
|
2216
|
+
const verdicts = /* @__PURE__ */ new WeakMap();
|
|
2217
|
+
/** Whether the client's server runs transactions, asked once per client; a failed probe is retried. */
|
|
2218
|
+
function transactionSupport(client) {
|
|
2219
|
+
let verdict = verdicts.get(client);
|
|
2220
|
+
if (!verdict) {
|
|
2221
|
+
verdict = probe(client).catch((error) => {
|
|
2222
|
+
verdicts.delete(client);
|
|
2223
|
+
throw error;
|
|
2224
|
+
});
|
|
2225
|
+
verdicts.set(client, verdict);
|
|
2226
|
+
}
|
|
2227
|
+
return verdict;
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* A replica-set member reports `setName` (an uninitiated `--replSet` member
|
|
2231
|
+
* `isreplicaset: true`), a mongos — also behind a load balancer —
|
|
2232
|
+
* `msg: 'isdbgrid'`. Anything else is a standalone `mongod`.
|
|
2233
|
+
*/
|
|
2234
|
+
async function probe(client) {
|
|
2235
|
+
const admin = client.db("admin");
|
|
2236
|
+
let reply;
|
|
2237
|
+
try {
|
|
2238
|
+
reply = await admin.command({ hello: 1 });
|
|
2239
|
+
} catch (error) {
|
|
2240
|
+
if (!(error instanceof mongodb.MongoServerError) || error.code !== 59) throw error;
|
|
2241
|
+
reply = await admin.command({ isMaster: 1 });
|
|
2242
|
+
}
|
|
2243
|
+
return reply.setName != null || reply.isreplicaset === true || reply.msg === "isdbgrid";
|
|
2244
|
+
}
|
|
2245
|
+
//#endregion
|
|
2215
2246
|
//#region src/lib/mongo-adapter.ts
|
|
2216
2247
|
/** Mirrors the `mongo.objectId` primitive's pattern — only definite hex ids convert. */
|
|
2217
2248
|
const OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;
|
|
@@ -2297,48 +2328,30 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
2297
2328
|
return other instanceof MongoAdapter && other._transactionOwner() === this._transactionOwner() && other.db.databaseName === this.db.databaseName;
|
|
2298
2329
|
}
|
|
2299
2330
|
/**
|
|
2300
|
-
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
2301
|
-
* Shared across all adapter instances for the same client so topology is probed once.
|
|
2302
|
-
*/
|
|
2303
|
-
static _txDisabledClients = /* @__PURE__ */ new WeakSet();
|
|
2304
|
-
get _txDisabled() {
|
|
2305
|
-
return this.client ? MongoAdapter._txDisabledClients.has(this.client) : true;
|
|
2306
|
-
}
|
|
2307
|
-
set _txDisabled(value) {
|
|
2308
|
-
if (value && this.client) MongoAdapter._txDisabledClients.add(this.client);
|
|
2309
|
-
}
|
|
2310
|
-
/**
|
|
2311
2331
|
* Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
|
|
2312
2332
|
* This handles txnNumber management and automatic retry for
|
|
2313
2333
|
* `TransientTransactionError` / `UnknownTransactionCommitResult`.
|
|
2314
2334
|
*
|
|
2335
|
+
* Transaction support is read from the server, not the connection mode: a
|
|
2336
|
+
* `directConnection=true` client to a replica-set primary runs real
|
|
2337
|
+
* transactions; a standalone `mongod` (or an adapter without a client)
|
|
2338
|
+
* runs `fn` without one.
|
|
2339
|
+
*
|
|
2315
2340
|
* `opts.isolation` (since 0.1.162): a MongoDB transaction reads one
|
|
2316
2341
|
* snapshot, which satisfies `'read committed'` and `'repeatable read'`;
|
|
2317
2342
|
* `'serializable'` throws `ISOLATION_NOT_SUPPORTED` (snapshot isolation
|
|
2318
|
-
* allows write skew), and so does any level
|
|
2319
|
-
*
|
|
2343
|
+
* allows write skew), and so does any level where `fn` would run without
|
|
2344
|
+
* a transaction.
|
|
2320
2345
|
*/
|
|
2321
2346
|
async withTransaction(fn, opts) {
|
|
2322
2347
|
const isolation = this._checkIsolation(opts);
|
|
2323
2348
|
if (this._getTransactionState()) return fn();
|
|
2324
|
-
const passThrough = () => {
|
|
2325
|
-
if (isolation) throw this._isolationNotSupported(`Isolation '${isolation}' needs a MongoDB transaction —
|
|
2349
|
+
const passThrough = (reason) => {
|
|
2350
|
+
if (isolation) throw this._isolationNotSupported(`Isolation '${isolation}' needs a MongoDB transaction — ${reason}`);
|
|
2326
2351
|
return fn();
|
|
2327
2352
|
};
|
|
2328
|
-
if (
|
|
2329
|
-
|
|
2330
|
-
const topology = this._client.topology;
|
|
2331
|
-
if (topology) {
|
|
2332
|
-
const type = (topology.description ?? topology.s?.description)?.type;
|
|
2333
|
-
if (type === "Single" || type === "Unknown") {
|
|
2334
|
-
this._txDisabled = true;
|
|
2335
|
-
return passThrough();
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
} catch {
|
|
2339
|
-
this._txDisabled = true;
|
|
2340
|
-
return passThrough();
|
|
2341
|
-
}
|
|
2353
|
+
if (!this._client) return passThrough("the adapter was created without a MongoClient");
|
|
2354
|
+
if (!await transactionSupport(this._client)) return passThrough("the server is a standalone mongod (transactions need a replica set or mongos)");
|
|
2342
2355
|
const session = this._client.startSession();
|
|
2343
2356
|
try {
|
|
2344
2357
|
let result;
|
package/dist/index.d.cts
CHANGED
|
@@ -318,23 +318,21 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
318
318
|
* client may serve several (since 0.1.147).
|
|
319
319
|
*/
|
|
320
320
|
sharesStoreWith(other: BaseDbAdapter): boolean;
|
|
321
|
-
/**
|
|
322
|
-
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
323
|
-
* Shared across all adapter instances for the same client so topology is probed once.
|
|
324
|
-
*/
|
|
325
|
-
private static readonly _txDisabledClients;
|
|
326
|
-
private get _txDisabled();
|
|
327
|
-
private set _txDisabled(value);
|
|
328
321
|
/**
|
|
329
322
|
* Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
|
|
330
323
|
* This handles txnNumber management and automatic retry for
|
|
331
324
|
* `TransientTransactionError` / `UnknownTransactionCommitResult`.
|
|
332
325
|
*
|
|
326
|
+
* Transaction support is read from the server, not the connection mode: a
|
|
327
|
+
* `directConnection=true` client to a replica-set primary runs real
|
|
328
|
+
* transactions; a standalone `mongod` (or an adapter without a client)
|
|
329
|
+
* runs `fn` without one.
|
|
330
|
+
*
|
|
333
331
|
* `opts.isolation` (since 0.1.162): a MongoDB transaction reads one
|
|
334
332
|
* snapshot, which satisfies `'read committed'` and `'repeatable read'`;
|
|
335
333
|
* `'serializable'` throws `ISOLATION_NOT_SUPPORTED` (snapshot isolation
|
|
336
|
-
* allows write skew), and so does any level
|
|
337
|
-
*
|
|
334
|
+
* allows write skew), and so does any level where `fn` would run without
|
|
335
|
+
* a transaction.
|
|
338
336
|
*/
|
|
339
337
|
withTransaction<T>(fn: () => Promise<T>, opts?: TTransactionOptions): Promise<T>;
|
|
340
338
|
private static readonly _ISOLATION_LEVELS;
|
package/dist/index.d.mts
CHANGED
|
@@ -318,23 +318,21 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
318
318
|
* client may serve several (since 0.1.147).
|
|
319
319
|
*/
|
|
320
320
|
sharesStoreWith(other: BaseDbAdapter): boolean;
|
|
321
|
-
/**
|
|
322
|
-
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
323
|
-
* Shared across all adapter instances for the same client so topology is probed once.
|
|
324
|
-
*/
|
|
325
|
-
private static readonly _txDisabledClients;
|
|
326
|
-
private get _txDisabled();
|
|
327
|
-
private set _txDisabled(value);
|
|
328
321
|
/**
|
|
329
322
|
* Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
|
|
330
323
|
* This handles txnNumber management and automatic retry for
|
|
331
324
|
* `TransientTransactionError` / `UnknownTransactionCommitResult`.
|
|
332
325
|
*
|
|
326
|
+
* Transaction support is read from the server, not the connection mode: a
|
|
327
|
+
* `directConnection=true` client to a replica-set primary runs real
|
|
328
|
+
* transactions; a standalone `mongod` (or an adapter without a client)
|
|
329
|
+
* runs `fn` without one.
|
|
330
|
+
*
|
|
333
331
|
* `opts.isolation` (since 0.1.162): a MongoDB transaction reads one
|
|
334
332
|
* snapshot, which satisfies `'read committed'` and `'repeatable read'`;
|
|
335
333
|
* `'serializable'` throws `ISOLATION_NOT_SUPPORTED` (snapshot isolation
|
|
336
|
-
* allows write skew), and so does any level
|
|
337
|
-
*
|
|
334
|
+
* allows write skew), and so does any level where `fn` would run without
|
|
335
|
+
* a transaction.
|
|
338
336
|
*/
|
|
339
337
|
withTransaction<T>(fn: () => Promise<T>, opts?: TTransactionOptions): Promise<T>;
|
|
340
338
|
private static readonly _ISOLATION_LEVELS;
|
package/dist/index.mjs
CHANGED
|
@@ -2211,6 +2211,37 @@ const validateMongoIdPlugin = (ctx, def, value) => {
|
|
|
2211
2211
|
}
|
|
2212
2212
|
};
|
|
2213
2213
|
//#endregion
|
|
2214
|
+
//#region src/lib/mongo-transaction-support.ts
|
|
2215
|
+
const verdicts = /* @__PURE__ */ new WeakMap();
|
|
2216
|
+
/** Whether the client's server runs transactions, asked once per client; a failed probe is retried. */
|
|
2217
|
+
function transactionSupport(client) {
|
|
2218
|
+
let verdict = verdicts.get(client);
|
|
2219
|
+
if (!verdict) {
|
|
2220
|
+
verdict = probe(client).catch((error) => {
|
|
2221
|
+
verdicts.delete(client);
|
|
2222
|
+
throw error;
|
|
2223
|
+
});
|
|
2224
|
+
verdicts.set(client, verdict);
|
|
2225
|
+
}
|
|
2226
|
+
return verdict;
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* A replica-set member reports `setName` (an uninitiated `--replSet` member
|
|
2230
|
+
* `isreplicaset: true`), a mongos — also behind a load balancer —
|
|
2231
|
+
* `msg: 'isdbgrid'`. Anything else is a standalone `mongod`.
|
|
2232
|
+
*/
|
|
2233
|
+
async function probe(client) {
|
|
2234
|
+
const admin = client.db("admin");
|
|
2235
|
+
let reply;
|
|
2236
|
+
try {
|
|
2237
|
+
reply = await admin.command({ hello: 1 });
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
if (!(error instanceof MongoServerError) || error.code !== 59) throw error;
|
|
2240
|
+
reply = await admin.command({ isMaster: 1 });
|
|
2241
|
+
}
|
|
2242
|
+
return reply.setName != null || reply.isreplicaset === true || reply.msg === "isdbgrid";
|
|
2243
|
+
}
|
|
2244
|
+
//#endregion
|
|
2214
2245
|
//#region src/lib/mongo-adapter.ts
|
|
2215
2246
|
/** Mirrors the `mongo.objectId` primitive's pattern — only definite hex ids convert. */
|
|
2216
2247
|
const OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;
|
|
@@ -2296,48 +2327,30 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
2296
2327
|
return other instanceof MongoAdapter && other._transactionOwner() === this._transactionOwner() && other.db.databaseName === this.db.databaseName;
|
|
2297
2328
|
}
|
|
2298
2329
|
/**
|
|
2299
|
-
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
2300
|
-
* Shared across all adapter instances for the same client so topology is probed once.
|
|
2301
|
-
*/
|
|
2302
|
-
static _txDisabledClients = /* @__PURE__ */ new WeakSet();
|
|
2303
|
-
get _txDisabled() {
|
|
2304
|
-
return this.client ? MongoAdapter._txDisabledClients.has(this.client) : true;
|
|
2305
|
-
}
|
|
2306
|
-
set _txDisabled(value) {
|
|
2307
|
-
if (value && this.client) MongoAdapter._txDisabledClients.add(this.client);
|
|
2308
|
-
}
|
|
2309
|
-
/**
|
|
2310
2330
|
* Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
|
|
2311
2331
|
* This handles txnNumber management and automatic retry for
|
|
2312
2332
|
* `TransientTransactionError` / `UnknownTransactionCommitResult`.
|
|
2313
2333
|
*
|
|
2334
|
+
* Transaction support is read from the server, not the connection mode: a
|
|
2335
|
+
* `directConnection=true` client to a replica-set primary runs real
|
|
2336
|
+
* transactions; a standalone `mongod` (or an adapter without a client)
|
|
2337
|
+
* runs `fn` without one.
|
|
2338
|
+
*
|
|
2314
2339
|
* `opts.isolation` (since 0.1.162): a MongoDB transaction reads one
|
|
2315
2340
|
* snapshot, which satisfies `'read committed'` and `'repeatable read'`;
|
|
2316
2341
|
* `'serializable'` throws `ISOLATION_NOT_SUPPORTED` (snapshot isolation
|
|
2317
|
-
* allows write skew), and so does any level
|
|
2318
|
-
*
|
|
2342
|
+
* allows write skew), and so does any level where `fn` would run without
|
|
2343
|
+
* a transaction.
|
|
2319
2344
|
*/
|
|
2320
2345
|
async withTransaction(fn, opts) {
|
|
2321
2346
|
const isolation = this._checkIsolation(opts);
|
|
2322
2347
|
if (this._getTransactionState()) return fn();
|
|
2323
|
-
const passThrough = () => {
|
|
2324
|
-
if (isolation) throw this._isolationNotSupported(`Isolation '${isolation}' needs a MongoDB transaction —
|
|
2348
|
+
const passThrough = (reason) => {
|
|
2349
|
+
if (isolation) throw this._isolationNotSupported(`Isolation '${isolation}' needs a MongoDB transaction — ${reason}`);
|
|
2325
2350
|
return fn();
|
|
2326
2351
|
};
|
|
2327
|
-
if (
|
|
2328
|
-
|
|
2329
|
-
const topology = this._client.topology;
|
|
2330
|
-
if (topology) {
|
|
2331
|
-
const type = (topology.description ?? topology.s?.description)?.type;
|
|
2332
|
-
if (type === "Single" || type === "Unknown") {
|
|
2333
|
-
this._txDisabled = true;
|
|
2334
|
-
return passThrough();
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
} catch {
|
|
2338
|
-
this._txDisabled = true;
|
|
2339
|
-
return passThrough();
|
|
2340
|
-
}
|
|
2352
|
+
if (!this._client) return passThrough("the adapter was created without a MongoClient");
|
|
2353
|
+
if (!await transactionSupport(this._client)) return passThrough("the server is a standalone mongod (transactions need a replica set or mongos)");
|
|
2341
2354
|
const session = this._client.startSession();
|
|
2342
2355
|
try {
|
|
2343
2356
|
let result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-mongo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.164",
|
|
4
4
|
"description": "Mongodb plugin for atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@atscript/typescript": "^0.1.109",
|
|
59
59
|
"@uniqu/core": "^0.1.15",
|
|
60
60
|
"mongodb": "^6.17.0",
|
|
61
|
-
"@atscript/db": "^0.1.
|
|
61
|
+
"@atscript/db": "^0.1.164"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "vp pack",
|