monsqlize 2.0.2 → 2.0.3

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.
@@ -2,6 +2,91 @@
2
2
 
3
3
  // src/capabilities/transaction/index.ts
4
4
  var import_node_crypto = require("node:crypto");
5
+
6
+ // src/core/errors/index.ts
7
+ var ErrorCodes = {
8
+ INVALID_ARGUMENT: "INVALID_ARGUMENT",
9
+ INVALID_COLLECTION_NAME: "INVALID_COLLECTION_NAME",
10
+ INVALID_DATABASE_NAME: "INVALID_DATABASE_NAME",
11
+ INVALID_EXPRESSION: "INVALID_EXPRESSION",
12
+ INVALID_PAGINATION: "INVALID_PAGINATION",
13
+ INVALID_OPERATION: "INVALID_OPERATION",
14
+ CACHE_UNAVAILABLE: "CACHE_UNAVAILABLE",
15
+ MANAGEMENT_OPERATION_FAILED: "MANAGEMENT_OPERATION_FAILED",
16
+ NOT_CONNECTED: "NOT_CONNECTED",
17
+ CONNECTION_FAILED: "CONNECTION_FAILED",
18
+ CONNECTION_CLOSED: "CONNECTION_CLOSED",
19
+ INVALID_CONFIG: "INVALID_CONFIG",
20
+ OPERATION_TIMEOUT: "OPERATION_TIMEOUT",
21
+ UNSUPPORTED_DATABASE: "UNSUPPORTED_DATABASE",
22
+ /** v1 compat: insertOne requires a non-null, non-array object document */
23
+ DOCUMENT_REQUIRED: "DOCUMENT_REQUIRED",
24
+ /** v1 compat: MongoDB duplicate key (error code 11000) */
25
+ DUPLICATE_KEY: "DUPLICATE_KEY",
26
+ /** v1 compat: general write failure (maps from MongoError in insert/update/delete) */
27
+ WRITE_ERROR: "WRITE_ERROR",
28
+ /** v1 compat: model-layer schema validation failure */
29
+ VALIDATION_ERROR: "VALIDATION_ERROR",
30
+ /** v1 compat: stream mode cannot use page jump (page > 1 with stream: true) */
31
+ STREAM_NO_JUMP: "STREAM_NO_JUMP",
32
+ /** v1 compat: stream mode cannot compute totals */
33
+ STREAM_NO_TOTALS: "STREAM_NO_TOTALS",
34
+ /** v1 compat: stream mode cannot use explain */
35
+ STREAM_NO_EXPLAIN: "STREAM_NO_EXPLAIN",
36
+ /** v1 compat: page jump exceeds the maxHops limit */
37
+ JUMP_TOO_FAR: "JUMP_TOO_FAR",
38
+ /** v1 compat: generic MongoDB driver error (maps numeric MongoDB error codes) */
39
+ MONGODB_ERROR: "MONGODB_ERROR",
40
+ /** v1 compat: cursor sort options mismatch between pages */
41
+ CURSOR_SORT_MISMATCH: "CURSOR_SORT_MISMATCH",
42
+ /** v1 compat: invalid or expired cursor token */
43
+ INVALID_CURSOR: "INVALID_CURSOR",
44
+ /** v1 compat: connection timeout */
45
+ CONNECTION_TIMEOUT: "CONNECTION_TIMEOUT",
46
+ /** v1 compat: generic database error */
47
+ DATABASE_ERROR: "DATABASE_ERROR",
48
+ /** v1 compat: query execution timeout */
49
+ QUERY_TIMEOUT: "QUERY_TIMEOUT",
50
+ /** v1 compat: cache backend error */
51
+ CACHE_ERROR: "CACHE_ERROR",
52
+ /** v1 compat: cache operation timeout */
53
+ CACHE_TIMEOUT: "CACHE_TIMEOUT",
54
+ /** v1 compat: model.define() called without schema */
55
+ MISSING_SCHEMA: "MISSING_SCHEMA",
56
+ /** v1 compat: model already registered under same name */
57
+ MODEL_ALREADY_EXISTS: "MODEL_ALREADY_EXISTS",
58
+ /** v1 compat: write requires at least one document */
59
+ DOCUMENTS_REQUIRED: "DOCUMENTS_REQUIRED",
60
+ /** v1 compat: concurrent write conflict in transaction */
61
+ WRITE_CONFLICT: "WRITE_CONFLICT",
62
+ /** v1 compat: business lock acquire failed */
63
+ LOCK_ACQUIRE_FAILED: "LOCK_ACQUIRE_FAILED",
64
+ /** v1 compat: business lock wait timeout */
65
+ LOCK_TIMEOUT: "LOCK_TIMEOUT",
66
+ /** v1 compat: model.model() called when not connected */
67
+ MODEL_NOT_DEFINED: "MODEL_NOT_DEFINED",
68
+ /** v1 compat: pool() called without pools configured */
69
+ NO_POOL_MANAGER: "NO_POOL_MANAGER",
70
+ /** v1 compat: pool() called with a pool name that does not exist */
71
+ POOL_NOT_FOUND: "POOL_NOT_FOUND",
72
+ /** v1 compat: model definition is not a valid object */
73
+ INVALID_MODEL_DEFINITION: "INVALID_MODEL_DEFINITION",
74
+ /** v1 compat: schema property is not a function or object */
75
+ INVALID_SCHEMA_TYPE: "INVALID_SCHEMA_TYPE"
76
+ };
77
+ function createError(code, message, details, cause) {
78
+ const error = new Error(message);
79
+ error.code = code;
80
+ if (details !== void 0) {
81
+ error.details = details;
82
+ }
83
+ if (cause !== void 0) {
84
+ error.cause = cause;
85
+ }
86
+ return error;
87
+ }
88
+
89
+ // src/capabilities/transaction/index.ts
5
90
  function toPublicTransactionStatus(state) {
6
91
  return state === "active" ? "started" : state;
7
92
  }
@@ -22,9 +107,9 @@ var Transaction = class {
22
107
  */
23
108
  async start() {
24
109
  if (this.state !== "pending") {
25
- throw new Error(`Cannot start transaction in state: ${this.state}`);
110
+ throw createError(ErrorCodes.INVALID_OPERATION, `Cannot start transaction in state: ${this.state}`);
26
111
  }
27
- this.session.startTransaction();
112
+ this.session.startTransaction(this.options.transactionOptions);
28
113
  this.state = "active";
29
114
  this.startedAt = Date.now();
30
115
  const timeout = this.options.timeout ?? 3e4;
@@ -44,7 +129,7 @@ var Transaction = class {
44
129
  */
45
130
  async commit() {
46
131
  if (this.state !== "active") {
47
- throw new Error(`Cannot commit transaction in state: ${this.state}`);
132
+ throw createError(ErrorCodes.INVALID_OPERATION, `Cannot commit transaction in state: ${this.state}`);
48
133
  }
49
134
  if (typeof this.session.commitTransaction === "function") {
50
135
  await this.session.commitTransaction();
@@ -2,6 +2,91 @@
2
2
 
3
3
  // src/capabilities/transaction/index.ts
4
4
  var import_node_crypto = require("node:crypto");
5
+
6
+ // src/core/errors/index.ts
7
+ var ErrorCodes = {
8
+ INVALID_ARGUMENT: "INVALID_ARGUMENT",
9
+ INVALID_COLLECTION_NAME: "INVALID_COLLECTION_NAME",
10
+ INVALID_DATABASE_NAME: "INVALID_DATABASE_NAME",
11
+ INVALID_EXPRESSION: "INVALID_EXPRESSION",
12
+ INVALID_PAGINATION: "INVALID_PAGINATION",
13
+ INVALID_OPERATION: "INVALID_OPERATION",
14
+ CACHE_UNAVAILABLE: "CACHE_UNAVAILABLE",
15
+ MANAGEMENT_OPERATION_FAILED: "MANAGEMENT_OPERATION_FAILED",
16
+ NOT_CONNECTED: "NOT_CONNECTED",
17
+ CONNECTION_FAILED: "CONNECTION_FAILED",
18
+ CONNECTION_CLOSED: "CONNECTION_CLOSED",
19
+ INVALID_CONFIG: "INVALID_CONFIG",
20
+ OPERATION_TIMEOUT: "OPERATION_TIMEOUT",
21
+ UNSUPPORTED_DATABASE: "UNSUPPORTED_DATABASE",
22
+ /** v1 compat: insertOne requires a non-null, non-array object document */
23
+ DOCUMENT_REQUIRED: "DOCUMENT_REQUIRED",
24
+ /** v1 compat: MongoDB duplicate key (error code 11000) */
25
+ DUPLICATE_KEY: "DUPLICATE_KEY",
26
+ /** v1 compat: general write failure (maps from MongoError in insert/update/delete) */
27
+ WRITE_ERROR: "WRITE_ERROR",
28
+ /** v1 compat: model-layer schema validation failure */
29
+ VALIDATION_ERROR: "VALIDATION_ERROR",
30
+ /** v1 compat: stream mode cannot use page jump (page > 1 with stream: true) */
31
+ STREAM_NO_JUMP: "STREAM_NO_JUMP",
32
+ /** v1 compat: stream mode cannot compute totals */
33
+ STREAM_NO_TOTALS: "STREAM_NO_TOTALS",
34
+ /** v1 compat: stream mode cannot use explain */
35
+ STREAM_NO_EXPLAIN: "STREAM_NO_EXPLAIN",
36
+ /** v1 compat: page jump exceeds the maxHops limit */
37
+ JUMP_TOO_FAR: "JUMP_TOO_FAR",
38
+ /** v1 compat: generic MongoDB driver error (maps numeric MongoDB error codes) */
39
+ MONGODB_ERROR: "MONGODB_ERROR",
40
+ /** v1 compat: cursor sort options mismatch between pages */
41
+ CURSOR_SORT_MISMATCH: "CURSOR_SORT_MISMATCH",
42
+ /** v1 compat: invalid or expired cursor token */
43
+ INVALID_CURSOR: "INVALID_CURSOR",
44
+ /** v1 compat: connection timeout */
45
+ CONNECTION_TIMEOUT: "CONNECTION_TIMEOUT",
46
+ /** v1 compat: generic database error */
47
+ DATABASE_ERROR: "DATABASE_ERROR",
48
+ /** v1 compat: query execution timeout */
49
+ QUERY_TIMEOUT: "QUERY_TIMEOUT",
50
+ /** v1 compat: cache backend error */
51
+ CACHE_ERROR: "CACHE_ERROR",
52
+ /** v1 compat: cache operation timeout */
53
+ CACHE_TIMEOUT: "CACHE_TIMEOUT",
54
+ /** v1 compat: model.define() called without schema */
55
+ MISSING_SCHEMA: "MISSING_SCHEMA",
56
+ /** v1 compat: model already registered under same name */
57
+ MODEL_ALREADY_EXISTS: "MODEL_ALREADY_EXISTS",
58
+ /** v1 compat: write requires at least one document */
59
+ DOCUMENTS_REQUIRED: "DOCUMENTS_REQUIRED",
60
+ /** v1 compat: concurrent write conflict in transaction */
61
+ WRITE_CONFLICT: "WRITE_CONFLICT",
62
+ /** v1 compat: business lock acquire failed */
63
+ LOCK_ACQUIRE_FAILED: "LOCK_ACQUIRE_FAILED",
64
+ /** v1 compat: business lock wait timeout */
65
+ LOCK_TIMEOUT: "LOCK_TIMEOUT",
66
+ /** v1 compat: model.model() called when not connected */
67
+ MODEL_NOT_DEFINED: "MODEL_NOT_DEFINED",
68
+ /** v1 compat: pool() called without pools configured */
69
+ NO_POOL_MANAGER: "NO_POOL_MANAGER",
70
+ /** v1 compat: pool() called with a pool name that does not exist */
71
+ POOL_NOT_FOUND: "POOL_NOT_FOUND",
72
+ /** v1 compat: model definition is not a valid object */
73
+ INVALID_MODEL_DEFINITION: "INVALID_MODEL_DEFINITION",
74
+ /** v1 compat: schema property is not a function or object */
75
+ INVALID_SCHEMA_TYPE: "INVALID_SCHEMA_TYPE"
76
+ };
77
+ function createError(code, message, details, cause) {
78
+ const error = new Error(message);
79
+ error.code = code;
80
+ if (details !== void 0) {
81
+ error.details = details;
82
+ }
83
+ if (cause !== void 0) {
84
+ error.cause = cause;
85
+ }
86
+ return error;
87
+ }
88
+
89
+ // src/capabilities/transaction/index.ts
5
90
  function toPublicTransactionStatus(state) {
6
91
  return state === "active" ? "started" : state;
7
92
  }
@@ -22,9 +107,9 @@ var Transaction = class {
22
107
  */
23
108
  async start() {
24
109
  if (this.state !== "pending") {
25
- throw new Error(`Cannot start transaction in state: ${this.state}`);
110
+ throw createError(ErrorCodes.INVALID_OPERATION, `Cannot start transaction in state: ${this.state}`);
26
111
  }
27
- this.session.startTransaction();
112
+ this.session.startTransaction(this.options.transactionOptions);
28
113
  this.state = "active";
29
114
  this.startedAt = Date.now();
30
115
  const timeout = this.options.timeout ?? 3e4;
@@ -44,7 +129,7 @@ var Transaction = class {
44
129
  */
45
130
  async commit() {
46
131
  if (this.state !== "active") {
47
- throw new Error(`Cannot commit transaction in state: ${this.state}`);
132
+ throw createError(ErrorCodes.INVALID_OPERATION, `Cannot commit transaction in state: ${this.state}`);
48
133
  }
49
134
  if (typeof this.session.commitTransaction === "function") {
50
135
  await this.session.commitTransaction();
@@ -142,7 +227,9 @@ var TransactionManager = class {
142
227
  this.stats = {
143
228
  totalTransactions: 0,
144
229
  successfulTransactions: 0,
145
- failedTransactions: 0
230
+ failedTransactions: 0,
231
+ readOnlyTransactions: 0,
232
+ writeTransactions: 0
146
233
  };
147
234
  const options = "client" in input ? input : {
148
235
  client: input,
@@ -153,6 +240,10 @@ var TransactionManager = class {
153
240
  this.cache = options.cache ?? null;
154
241
  this.logger = options.logger ?? null;
155
242
  this.lockManager = options.lockManager ?? null;
243
+ this.defaultReadConcern = options.defaultReadConcern;
244
+ this.defaultWriteConcern = options.defaultWriteConcern;
245
+ this.defaultReadPreference = options.defaultReadPreference;
246
+ this.maxStatsSamples = options.maxStatsSamples ?? 1e3;
156
247
  this.defaultOptions = {
157
248
  maxDuration: options.maxDuration ?? 3e4,
158
249
  enableRetry: options.enableRetry ?? true,
@@ -169,11 +260,17 @@ var TransactionManager = class {
169
260
  const session = this.client.startSession({
170
261
  causalConsistency: options.causalConsistency !== false
171
262
  });
263
+ const transactionOptions = {
264
+ readConcern: options.readConcern ?? this.defaultReadConcern,
265
+ writeConcern: options.writeConcern ?? this.defaultWriteConcern,
266
+ readPreference: options.readPreference ?? this.defaultReadPreference
267
+ };
172
268
  const transaction = new Transaction(session, {
173
269
  cache: this.cache,
174
270
  logger: this.logger,
175
271
  lockManager: options.enableCacheLock === false ? null : this.lockManager,
176
- timeout: options.timeout ?? options.maxDuration ?? this.defaultOptions.maxDuration
272
+ timeout: options.timeout ?? options.maxDuration ?? this.defaultOptions.maxDuration,
273
+ transactionOptions: compactUndefined(transactionOptions)
177
274
  });
178
275
  const originalEnd = transaction.end.bind(transaction);
179
276
  transaction.end = async () => {
@@ -200,12 +297,12 @@ var TransactionManager = class {
200
297
  await transaction.start();
201
298
  const result = await callback(transaction);
202
299
  await transaction.commit();
203
- this.recordStats(Date.now() - startedAt, true);
300
+ this.recordStats(transaction, Date.now() - startedAt, true);
204
301
  return result;
205
302
  } catch (error) {
206
303
  lastError = error;
207
304
  await transaction.abort();
208
- this.recordStats(Date.now() - startedAt, false);
305
+ this.recordStats(transaction, Date.now() - startedAt, false);
209
306
  if (!enableRetry || attempt === maxRetries || !isTransientTransactionError(error)) {
210
307
  throw error;
211
308
  }
@@ -241,27 +338,54 @@ var TransactionManager = class {
241
338
  */
242
339
  getStats() {
243
340
  const averageDuration = this.durations.length === 0 ? 0 : this.durations.reduce((sum, item) => sum + item, 0) / this.durations.length;
341
+ const sortedDurations = [...this.durations].sort((a, b) => a - b);
342
+ const p95Duration = percentile(sortedDurations, 0.95);
343
+ const p99Duration = percentile(sortedDurations, 0.99);
344
+ const totalTransactions = this.stats.totalTransactions;
244
345
  return {
245
- totalTransactions: this.stats.totalTransactions,
346
+ totalTransactions,
246
347
  successfulTransactions: this.stats.successfulTransactions,
247
348
  failedTransactions: this.stats.failedTransactions,
349
+ readOnlyTransactions: this.stats.readOnlyTransactions,
350
+ writeTransactions: this.stats.writeTransactions,
248
351
  activeTransactions: this.activeTransactions.size,
249
- averageDuration
352
+ averageDuration,
353
+ p95Duration,
354
+ p99Duration,
355
+ successRate: totalTransactions > 0 ? `${(this.stats.successfulTransactions / totalTransactions * 100).toFixed(2)}%` : "0%",
356
+ readOnlyRatio: totalTransactions > 0 ? `${(this.stats.readOnlyTransactions / totalTransactions * 100).toFixed(2)}%` : "0%",
357
+ sampleCount: this.durations.length
250
358
  };
251
359
  }
252
- recordStats(duration, success) {
360
+ recordStats(transaction, duration, success) {
253
361
  this.stats.totalTransactions += 1;
254
362
  if (success) {
255
363
  this.stats.successfulTransactions += 1;
256
364
  } else {
257
365
  this.stats.failedTransactions += 1;
258
366
  }
367
+ if (transaction.pendingInvalidations.size > 0) {
368
+ this.stats.writeTransactions += 1;
369
+ } else {
370
+ this.stats.readOnlyTransactions += 1;
371
+ }
259
372
  this.durations.push(duration);
260
- if (this.durations.length > 100) {
373
+ if (this.durations.length > this.maxStatsSamples) {
261
374
  this.durations.shift();
262
375
  }
263
376
  }
264
377
  };
378
+ function percentile(sortedValues, ratio) {
379
+ if (sortedValues.length === 0) {
380
+ return 0;
381
+ }
382
+ const index = Math.floor(sortedValues.length * ratio);
383
+ return sortedValues[Math.min(index, sortedValues.length - 1)] ?? 0;
384
+ }
385
+ function compactUndefined(value) {
386
+ const entries = Object.entries(value).filter(([, item]) => item !== void 0);
387
+ return entries.length === 0 ? void 0 : Object.fromEntries(entries);
388
+ }
265
389
  function stringifySessionId(id) {
266
390
  if (typeof id === "string") {
267
391
  return id;