@pi-r/mongodb 0.7.3 → 0.8.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/client/index.js CHANGED
@@ -1,79 +1,23 @@
1
1
  "use strict";
2
- exports.DB_SOURCE_TYPE = exports.DB_SOURCE_CLIENT = exports.getSortValue = exports.getFilterValue = exports.checkTimeout = exports.executeBatchQuery = exports.executeQuery = exports.setCredential = void 0;
2
+ exports.DB_SOURCE_TYPE = exports.DB_SOURCE_CLIENT = void 0;
3
+ exports.setCredential = setCredential;
4
+ exports.executeQuery = executeQuery;
5
+ exports.executeBatchQuery = executeBatchQuery;
6
+ exports.checkTimeout = checkTimeout;
7
+ exports.getFilterValue = getFilterValue;
8
+ exports.getSortValue = getSortValue;
3
9
  const path = require("path");
4
10
  const mongodb = require("mongodb");
5
- const types_1 = require("@e-mc/types");
6
- const util_1 = require("@e-mc/db/util");
7
11
  const Db = require("@e-mc/db");
8
12
  const Request = require("@e-mc/request");
9
- const DbPool = require('@e-mc/db/pool');
10
- class MongoDBPool extends DbPool {
11
- async getConnection() {
12
- return this.client.connect();
13
- }
14
- async detach(force) {
15
- this.remove();
16
- if (this.closed) {
17
- return;
18
- }
19
- return this.close(force);
20
- }
21
- async close(force = false) {
22
- return this.client.close(force);
23
- }
24
- isEmpty() {
25
- if (this.closed) {
26
- return true;
27
- }
28
- const topology = this.client.topology;
29
- if (topology) {
30
- const name = Object.getOwnPropertySymbols(topology).find(sym => sym.toString() === 'Symbol(waitQueue)');
31
- return (0, util_1.checkEmpty)(name ? topology[name] : topology.s.servers);
32
- }
33
- return false;
34
- }
35
- get closeable() {
36
- const topology = this.client.topology;
37
- return topology ? super.closeable || topology.s.state === 'closing' : true;
38
- }
39
- get closed() {
40
- const topology = this.client.topology;
41
- if (topology && (this.success > 0 || this.failed > 0)) {
42
- return topology.isDestroyed();
43
- }
44
- return this.client.s?.hasBeenClosed === true;
45
- }
46
- }
13
+ const types_1 = require("@e-mc/types");
14
+ const util_1 = require("@e-mc/db/util");
15
+ const DbPool = require("@pi-r/mongodb/client/pool");
47
16
  const POOL_STATE = {};
48
- const POOL_UNUSED = ['maxPoolSize', 'minPoolSize', 'connectTimeoutMS', 'socketTimeoutMS', 'maxConnecting', 'maxIdleTimeMS', 'waitQueueTimeoutMS', 'serverSelectionTimeoutMS', 'heartbeatFrequencyMS', 'minHeartbeatFrequencyMS', 'compressors', 'zlibCompressionLevel', 'srvMaxHosts'];
49
- const POOL_ACTIVE = new WeakSet();
50
- function removePoolProperties(credential) {
51
- const options = credential.options;
52
- if (options && !credential.uuidKey && POOL_ACTIVE.has(credential)) {
53
- let result;
54
- for (let i = 0, length = POOL_UNUSED.length; i < length; ++i) {
55
- const attr = POOL_UNUSED[i];
56
- if (attr in options) {
57
- result ||= { ...options };
58
- delete result[attr];
59
- }
60
- }
61
- if (result) {
62
- return { uri: credential.uri, options: result };
63
- }
64
- }
65
- return credential;
66
- }
67
17
  function getCacheObject(item) {
68
18
  const credential = item.credential;
69
19
  return { uri: item.uri, options: item.options, uuidKey: credential?.uuidKey };
70
20
  }
71
- function getPoolKey(credential) {
72
- const { options, uri } = credential;
73
- if (uri && options) {
74
- return uri + JSON.stringify(credential.options);
75
- }
76
- }
77
21
  function setCredential(item) {
78
22
  const credential = this.getCredential(item);
79
23
  const options = item.options ||= {};
@@ -110,24 +54,54 @@ function setCredential(item) {
110
54
  uri += (uri.endsWith('?') ? '' : '&') + 'authMechanism=' + encodeURIComponent(authMechanism);
111
55
  switch (authMechanism) {
112
56
  case 'MONGODB-X509': {
113
- const checkFile = (value) => {
114
- if ((0, types_1.isString)(value) && !Request.isCert(value = value.trim()) && Db.isPath(value = path.resolve(value)) && this.canRead(value, { ownPermissionOnly: true })) {
115
- return value;
57
+ for (const attr of ['tlsCertificateKeyFile', 'tlsCAFile', 'tlsCRLFile']) {
58
+ let value = credential[attr];
59
+ if ((0, types_1.isString)(value)) {
60
+ if (Db.isPath(value = value.trim(), true)) {
61
+ const pathname = path.resolve(value);
62
+ if (this.canRead(pathname, { ownPermissionOnly: true })) {
63
+ options[attr] = pathname;
64
+ continue;
65
+ }
66
+ }
67
+ else if (Request.isCert(value)) {
68
+ if (attr === 'tlsCertificateKeyFile') {
69
+ const pattern = /-+([^-]+)-+\s+[^-]+-+[^-]+-+/g;
70
+ const key = [];
71
+ const cert = [];
72
+ let match;
73
+ while (match = pattern.exec(value)) {
74
+ if (match[1].includes('BEGIN')) {
75
+ if (match[1].includes('KEY')) {
76
+ key.push(match[0]);
77
+ }
78
+ else {
79
+ cert.push(match[0]);
80
+ }
81
+ }
82
+ }
83
+ if (key.length > 0) {
84
+ options.key = Buffer.from(key.join('\n'), 'utf-8');
85
+ }
86
+ if (cert.length > 0) {
87
+ options.cert = Buffer.from(cert.join('\n'), 'utf-8');
88
+ }
89
+ }
90
+ else {
91
+ options[attr === 'tlsCAFile' ? 'ca' : 'crl'] = Buffer.from(value, 'utf-8');
92
+ }
93
+ }
116
94
  }
117
- return '';
118
- };
119
- let { tlsCertificateKeyFile, tlsCAFile, tlsCRLFile } = credential;
120
- if (tlsCertificateKeyFile && (tlsCertificateKeyFile = checkFile(tlsCertificateKeyFile))) {
95
+ }
96
+ if (options.tlsCertificateKeyFile || credential.cert && credential.key) {
121
97
  const { tlsCertificateKeyFilePassword, tlsAllowInvalidHostnames, tlsAllowInvalidCertificates, tlsInsecure } = credential;
122
- options.tlsCertificateKeyFile = tlsCertificateKeyFile;
123
- if (tlsCAFile = checkFile(tlsCAFile)) {
124
- options.tlsCAFile = tlsCAFile;
125
- }
126
- if (tlsCRLFile = checkFile(tlsCRLFile)) {
127
- options.tlsCRLFile = tlsCRLFile;
128
- }
129
98
  if (tlsCertificateKeyFilePassword) {
130
- options.tlsCertificateKeyFilePassword = tlsCertificateKeyFilePassword;
99
+ if (credential.tlsCertificateKeyFile) {
100
+ options.tlsCertificateKeyFilePassword = tlsCertificateKeyFilePassword;
101
+ }
102
+ else {
103
+ options.passphrase = tlsCertificateKeyFilePassword;
104
+ }
131
105
  }
132
106
  if (typeof tlsAllowInvalidHostnames === 'boolean') {
133
107
  options.tlsAllowInvalidHostnames = tlsAllowInvalidHostnames;
@@ -164,6 +138,12 @@ function setCredential(item) {
164
138
  uri += '&authSource=' + encodeURIComponent(authSource);
165
139
  }
166
140
  item.uri = uri;
141
+ if (!options.writeConcern && ('w' in options || 'wtimeoutMS' in options || 'journal' in options)) {
142
+ options.writeConcern = new mongodb.WriteConcern(options.w, options.wtimeoutMS, options.journal);
143
+ delete options.w;
144
+ delete options.wtimeoutMS;
145
+ delete options.journal;
146
+ }
167
147
  }
168
148
  else if (!(0, types_1.isString)(uri)) {
169
149
  throw (0, types_1.errorMessage)("mongodb", 'Not defined - credentials');
@@ -211,18 +191,19 @@ function setCredential(item) {
211
191
  options.socketTimeoutMS ??= socket_timeout;
212
192
  }
213
193
  }
214
- const poolKey = getPoolKey(item);
194
+ const poolKey = DbPool.asString(item);
215
195
  if ((pool = POOL_STATE[poolKey]) && !pool.closed) {
216
196
  pool.add(item);
217
197
  return;
218
198
  }
219
- new MongoDBPool(new mongodb.MongoClient(item.uri, options), poolKey, username && password ? { username, password } : undefined).add(item).parent = POOL_STATE;
199
+ const client = new mongodb.MongoClient(item.uri, options);
200
+ new DbPool(client, poolKey, username && password ? { username, password } : undefined)
201
+ .add(item)
202
+ .parent = POOL_STATE;
220
203
  }
221
- exports.setCredential = setCredential;
222
204
  async function executeQuery(item, options) {
223
205
  return (await executeBatchQuery.call(this, [item], options))[0] || [];
224
206
  }
225
- exports.executeQuery = executeQuery;
226
207
  async function executeBatchQuery(batch, options = '', outResult) {
227
208
  const length = batch.length;
228
209
  if (length === 0) {
@@ -243,7 +224,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
243
224
  parallel = false;
244
225
  }
245
226
  else if (parallel === undefined) {
246
- parallel = !batch.some(item => item.parallel === false || !!item.update);
227
+ parallel = !batch.some(item => item.parallel === false);
247
228
  }
248
229
  if (!parallel) {
249
230
  outResult ||= new Array(length);
@@ -254,13 +235,12 @@ async function executeBatchQuery(batch, options = '', outResult) {
254
235
  let mongoClient, mongoCredential, mongoCache = 0, onceCredential = connectOnce ? getCacheObject(batch[0]) : undefined;
255
236
  const getConnection = async (item, credential) => {
256
237
  item.transactionState = 64;
257
- const pool = item.usePool && DbPool.findKey(POOL_STATE, item.usePool, getPoolKey(connectOnce ? batch[0] : item), ...connectOnce ? [item, batch[0]] : [item]);
238
+ const pool = item.usePool && DbPool.findKey(POOL_STATE, item.usePool, DbPool.asString(connectOnce ? batch[0] : item), ...connectOnce ? [item, batch[0]] : [item]);
258
239
  let client;
259
240
  if (pool) {
260
241
  try {
261
- client = await pool.getConnection();
242
+ client = await pool.getConnection(credential);
262
243
  pool.connected = true;
263
- POOL_ACTIVE.add(credential);
264
244
  }
265
245
  catch {
266
246
  if (pool.closeable) {
@@ -334,7 +314,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
334
314
  }
335
315
  if (queryString) {
336
316
  const setResult = () => {
337
- if (ignoreCache !== 1 && (rows = this.getQueryResult(source, removePoolProperties(credential), queryString, cacheValue))) {
317
+ if (ignoreCache !== 1 && (rows = this.getQueryResult(source, DbPool.sanitize(credential), queryString, cacheValue))) {
338
318
  ++mongoCache;
339
319
  if (parallel) {
340
320
  tasks[i] = Promise.resolve(rows);
@@ -374,10 +354,13 @@ async function executeBatchQuery(batch, options = '', outResult) {
374
354
  let commandType;
375
355
  try {
376
356
  const instance = mongoClient || await getConnection(item, credential);
377
- const command = item.command;
378
- if (command) {
379
- await Promise.all((!Array.isArray(command) ? [command] : command).map(async (cmd) => runCommand(instance, cmd)))
380
- .then(items => items.forEach(output => this.addLog(this.statusType.INFO, output, "mongodb", 'runCommand')));
357
+ if (item.command) {
358
+ await Promise.all((!Array.isArray(item.command) ? [item.command] : item.command).map(async (cmd) => runCommand(instance, cmd)))
359
+ .then(items => {
360
+ for (const output of items) {
361
+ this.addLog(this.statusType.INFO, output, "mongodb", 'runCommand');
362
+ }
363
+ });
381
364
  }
382
365
  const collection = getCollection(instance);
383
366
  if (update && coercing) {
@@ -402,7 +385,9 @@ async function executeBatchQuery(batch, options = '', outResult) {
402
385
  if (pipeline) {
403
386
  const aggregateOptions = (0, types_1.isPlainObject)(aggregate) ? aggregate.options : undefined;
404
387
  if (coercing) {
405
- pipeline.forEach(doc => (0, types_1.coerceObject)(doc));
388
+ for (const doc of pipeline) {
389
+ (0, types_1.coerceObject)(doc);
390
+ }
406
391
  if (aggregateOptions) {
407
392
  (0, types_1.coerceObject)(aggregateOptions);
408
393
  }
@@ -457,7 +442,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
457
442
  }
458
443
  }
459
444
  if (streamRow) {
460
- const processRow = typeof streamRow === 'function' && streamRow;
445
+ const processRow = typeof streamRow === 'function' ? streamRow : null;
461
446
  const stream = cursor.stream();
462
447
  let error;
463
448
  rows = [];
@@ -482,7 +467,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
482
467
  }
483
468
  else {
484
469
  this.add(item, 4);
485
- resolve(!error ? this.setQueryResult(source, removePoolProperties(mongoCredential || credential), queryString, rows, cacheValue) : rows);
470
+ resolve(!error ? this.setQueryResult(source, DbPool.sanitize(mongoCredential || credential), queryString, rows, cacheValue) : rows);
486
471
  }
487
472
  })
488
473
  .on('end', () => stream.destroy());
@@ -496,7 +481,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
496
481
  if (targetObject) {
497
482
  rows = targetObject(item, rows);
498
483
  }
499
- resolve(this.setQueryResult(source, removePoolProperties(mongoCredential || credential), queryString, rows, cacheValue));
484
+ resolve(this.setQueryResult(source, DbPool.sanitize(mongoCredential || credential), queryString, rows, cacheValue));
500
485
  }
501
486
  else {
502
487
  throw (0, types_1.errorMessage)(source, "Missing database query");
@@ -522,17 +507,17 @@ async function executeBatchQuery(batch, options = '', outResult) {
522
507
  }
523
508
  }
524
509
  return this.processRows(batch, tasks, {
525
- disconnect: () => clients.forEach(item => {
526
- item.close(true);
527
- }),
528
- parallel
510
+ parallel,
511
+ disconnect() {
512
+ for (const item of clients) {
513
+ void item.close(true);
514
+ }
515
+ }
529
516
  }, outResult);
530
517
  }
531
- exports.executeBatchQuery = executeBatchQuery;
532
518
  async function checkTimeout(value, limit = 0) {
533
519
  return DbPool.checkTimeout(POOL_STATE, value, limit);
534
520
  }
535
- exports.checkTimeout = checkTimeout;
536
521
  function getFilterValue(query, coerce) {
537
522
  let value, options;
538
523
  if ((0, types_1.isObject)(query) && query.value && query.options) {
@@ -549,7 +534,6 @@ function getFilterValue(query, coerce) {
549
534
  }
550
535
  return [value, options];
551
536
  }
552
- exports.getFilterValue = getFilterValue;
553
537
  function getSortValue(sort, coerce) {
554
538
  let value, direction;
555
539
  if ((0, types_1.isObject)(sort) && sort.value && sort.direction) {
@@ -563,6 +547,5 @@ function getSortValue(sort, coerce) {
563
547
  }
564
548
  return [value, direction];
565
549
  }
566
- exports.getSortValue = getSortValue;
567
550
  exports.DB_SOURCE_CLIENT = true;
568
551
  exports.DB_SOURCE_TYPE = types_1.DB_TYPE.NOSQL | types_1.DB_TYPE.DOCUMENT;
@@ -0,0 +1,9 @@
1
+ import type { DbPoolConstructor } from '@e-mc/types/lib/db';
2
+
3
+ import type { DbPoolCredential, MongoDBDataSource } from '../types';
4
+
5
+ import type { MongoClient } from 'mongodb';
6
+
7
+ declare const MongoDBPool: DbPoolConstructor<MongoDBDataSource, MongoClient, MongoClient, DbPoolCredential>;
8
+
9
+ export = MongoDBPool;
package/client/pool.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ const util_1 = require("@e-mc/db/util");
3
+ const DbPool = require('@e-mc/db/pool');
4
+ const POOL_ACTIVE = new WeakSet();
5
+ class MongoDBPool extends DbPool {
6
+ static CACHE_UNUSED = ['maxPoolSize', 'minPoolSize', 'connectTimeoutMS', 'socketTimeoutMS', 'maxConnecting', 'maxIdleTimeMS', 'waitQueueTimeoutMS', 'maxStalenessSeconds', 'localThresholdMS', 'serverSelectionTimeoutMS', 'heartbeatFrequencyMS', 'minHeartbeatFrequencyMS', 'compressors', 'zlibCompressionLevel', 'srvMaxHosts'];
7
+ static asString(credential) {
8
+ const { options, uri } = credential;
9
+ if (uri && options) {
10
+ return uri + JSON.stringify(options);
11
+ }
12
+ return super.asString(credential);
13
+ }
14
+ static sanitize(credential) {
15
+ const target = credential.options;
16
+ if (!target || !POOL_ACTIVE.has(credential) || credential.uuidKey) {
17
+ return credential;
18
+ }
19
+ const options = super.sanitize(target);
20
+ return options !== target ? { uri: credential.uri, options } : credential;
21
+ }
22
+ async detach(force) {
23
+ this.remove();
24
+ if (this.closed) {
25
+ return;
26
+ }
27
+ return this.close(force);
28
+ }
29
+ get closeable() {
30
+ const topology = this.client.topology;
31
+ return topology ? super.closeable || topology.s.state === 'closing' : true;
32
+ }
33
+ async getConnection(credential) {
34
+ if (credential) {
35
+ POOL_ACTIVE.add(credential);
36
+ }
37
+ return this.client.connect();
38
+ }
39
+ async close(force = false) {
40
+ return this.client.close(force);
41
+ }
42
+ isEmpty() {
43
+ if (this.closed) {
44
+ return true;
45
+ }
46
+ const topology = this.client.topology;
47
+ if (topology) {
48
+ const name = Object.getOwnPropertySymbols(topology).find(sym => sym.toString() === 'Symbol(waitQueue)');
49
+ return (0, util_1.checkEmpty)(name ? topology[name] : topology.s.servers);
50
+ }
51
+ return false;
52
+ }
53
+ get closed() {
54
+ const topology = this.client.topology;
55
+ if (topology && (this.success > 0 || this.failed > 0)) {
56
+ return topology.isDestroyed();
57
+ }
58
+ return this.client.s?.hasBeenClosed === true;
59
+ }
60
+ }
61
+ module.exports = MongoDBPool;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/mongodb",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "description": "MongoDB client driver for E-mc.",
5
5
  "main": "client/index.js",
6
6
  "types": "client/index.d.ts",
@@ -21,9 +21,9 @@
21
21
  "license": "MIT",
22
22
  "homepage": "https://github.com/anpham6/pi-r#readme",
23
23
  "dependencies": {
24
- "@e-mc/db": "^0.9.6",
25
- "@e-mc/request": "^0.9.6",
26
- "@e-mc/types": "^0.9.6",
24
+ "@e-mc/db": "^0.10.0",
25
+ "@e-mc/request": "^0.10.0",
26
+ "@e-mc/types": "^0.10.0",
27
27
  "mongodb": "^6.8.0"
28
28
  }
29
29
  }
package/types/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { DbDataSource } from '@e-mc/types/lib/squared';
2
2
 
3
+ import type { IdentifierAction } from '@e-mc/types/lib/core';
3
4
  import type { CascadeAction, ServerAuth } from '@e-mc/types/lib/db';
4
5
 
5
6
  import type { AggregateOptions, CollectionOptions, CommandOperationOptions, DbOptions, Document, Filter, MongoClientOptions, OptionalUnlessRequiredId, RunCommandOptions, Sort, SortDirection, UpdateFilter } from 'mongodb';
@@ -40,4 +41,9 @@ export interface MongoDBClientDBOptions {
40
41
  export interface MongoDBFilterValue {
41
42
  value?: Filter<Document>;
42
43
  options?: CommandOperationOptions;
44
+ }
45
+
46
+ export interface DbPoolCredential extends IdentifierAction {
47
+ uri?: string;
48
+ options?: MongoClientOptions;
43
49
  }