@xylabs/mongo 4.11.1 → 4.11.2

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.
@@ -1,27 +1,14 @@
1
1
  // src/Base.ts
2
- import { assertEx as assertEx2 } from "@xylabs/assert";
2
+ import { assertEx } from "@xylabs/assert";
3
3
 
4
4
  // src/Wrapper.ts
5
- import { assertEx } from "@xylabs/assert";
6
- import { delay } from "@xylabs/delay";
7
- import { forget } from "@xylabs/forget";
8
- import { Mutex } from "async-mutex";
9
5
  import { MongoClient } from "mongodb";
10
6
  var MongoClientWrapper = class _MongoClientWrapper {
11
7
  static clients = /* @__PURE__ */ new Map();
12
- checkFrequency = 100;
13
8
  client;
14
- closeDelay;
15
- connected = false;
16
- connectionMutex = new Mutex();
17
- connections = 0;
18
- delayCount = 0;
19
- delayedCloseMutex = new Mutex();
20
- uri;
21
9
  constructor(uri, maxPoolSize, closeDelay) {
22
- this.uri = uri;
23
- this.client = new MongoClient(uri, { maxPoolSize });
24
- this.closeDelay = closeDelay ?? 10 * 1e3;
10
+ const options = { maxPoolSize, maxIdleTimeMS: closeDelay };
11
+ this.client = new MongoClient(uri, options);
25
12
  }
26
13
  static get(uri, poolSize, closeDelay) {
27
14
  let client = this.clients.get(uri);
@@ -32,54 +19,13 @@ var MongoClientWrapper = class _MongoClientWrapper {
32
19
  return client;
33
20
  }
34
21
  async connect() {
35
- return await this.connectionMutex.runExclusive(async () => {
36
- if (this.connections === 0 && !this.connected) {
37
- await this.client.connect();
38
- this.connected = true;
39
- }
40
- this.connections += 1;
41
- return this.client;
42
- });
22
+ return await Promise.resolve(this.client);
43
23
  }
44
24
  async disconnect() {
45
- return await this.connectionMutex.runExclusive(() => {
46
- assertEx(this.connections > 0, () => "Unexpected disconnect");
47
- this.connections -= 1;
48
- if (this.connections === 0) {
49
- forget(this.initiateClose());
50
- }
51
- return this.connections;
52
- });
25
+ return await Promise.resolve(0);
53
26
  }
54
27
  async initiateClose() {
55
- const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {
56
- const alreadyStarted2 = this.delayCount > 0;
57
- this.delayCount = Math.floor(this.closeDelay / this.checkFrequency);
58
- return alreadyStarted2;
59
- });
60
- if (!alreadyStarted) {
61
- while (this.delayCount > 0) {
62
- await this.delayedCloseMutex.runExclusive(async () => {
63
- if (this.connections > 0 || !this.connected) {
64
- this.delayCount = 0;
65
- } else if (this.delayCount === 1) {
66
- await this.close();
67
- this.delayCount = 0;
68
- } else {
69
- this.delayCount -= 1;
70
- }
71
- await delay(this.checkFrequency);
72
- });
73
- }
74
- }
75
- }
76
- async close() {
77
- return await this.connectionMutex.runExclusive(async () => {
78
- assertEx(this.connected, () => "Unexpected close");
79
- this.connected = false;
80
- await this.client.close(true);
81
- _MongoClientWrapper.clients.delete(this.uri);
82
- });
28
+ await Promise.resolve();
83
29
  }
84
30
  };
85
31
 
@@ -135,7 +81,7 @@ var BaseMongoSdk = class {
135
81
  async useMongo(func) {
136
82
  const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay);
137
83
  const connection = await wrapper.connect();
138
- assertEx2(connection, () => "Connection failed");
84
+ assertEx(connection, () => "Connection failed");
139
85
  try {
140
86
  return await func(connection);
141
87
  } finally {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Base.ts","../../src/Wrapper.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type {\n BulkWriteOptions,\n Collection,\n DeleteResult,\n Document,\n Filter,\n FindCursor,\n InsertOneOptions,\n MongoClient,\n OptionalUnlessRequiredId,\n UpdateFilter,\n WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\nexport class BaseMongoSdk<T extends Document> {\n config: BaseMongoSdkConfig\n\n constructor(config: BaseMongoSdkConfig) {\n this.config = config\n }\n\n get uri() {\n return (\n this.config.dbConnectionString\n // eslint-disable-next-line @stylistic/max-len\n ?? `mongodb+srv://${this.config.dbUserName}:${this.config.dbPassword}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n )\n }\n\n async deleteOne(filter: Filter<T>) {\n return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n return await collection.deleteOne(filter)\n })\n }\n\n async find(filter: Filter<T>) {\n return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n return collection.find(filter)\n })\n }\n\n async findOne(filter: Filter<T>) {\n return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n return await collection.findOne(filter)\n })\n }\n\n async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertMany(items, options)\n })\n }\n\n async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertOne(item, options)\n })\n }\n\n async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: false })\n })\n }\n\n async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: true })\n })\n }\n\n async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n return await this.useMongo<R>(async (client: MongoClient) => {\n return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n })\n }\n\n async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n const connection = await wrapper.connect()\n assertEx(connection, () => 'Connection failed')\n try {\n return await func(connection)\n } finally {\n await wrapper.disconnect()\n }\n }\n}\n","import { assertEx } from '@xylabs/assert'\nimport { delay } from '@xylabs/delay'\nimport { forget } from '@xylabs/forget'\nimport { Mutex } from 'async-mutex'\nimport type { MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\n\nexport class MongoClientWrapper {\n static readonly clients = new Map<string, MongoClientWrapper>()\n\n private checkFrequency = 100\n private client: MongoClient\n private closeDelay\n private connected = false\n private connectionMutex = new Mutex()\n private connections = 0\n private delayCount = 0\n private delayedCloseMutex = new Mutex()\n\n private uri: string\n\n constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {\n this.uri = uri\n this.client = new MongoClient(uri, { maxPoolSize } as MongoClientOptions)\n this.closeDelay = closeDelay ?? 10 * 1000 /* 10 seconds default */\n }\n\n static get(uri: string, poolSize?: number, closeDelay?: number) {\n let client = this.clients.get(uri)\n if (!client) {\n client = new MongoClientWrapper(uri, poolSize, closeDelay)\n this.clients.set(uri, client)\n }\n return client\n }\n\n async connect() {\n return await this.connectionMutex.runExclusive<MongoClient>(async () => {\n if (this.connections === 0 && !this.connected) {\n await this.client.connect()\n this.connected = true\n }\n this.connections += 1\n return this.client\n })\n }\n\n async disconnect() {\n return await this.connectionMutex.runExclusive(() => {\n assertEx(this.connections > 0, () => 'Unexpected disconnect')\n this.connections -= 1\n if (this.connections === 0) {\n forget(this.initiateClose())\n }\n return this.connections\n })\n }\n\n async initiateClose() {\n const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {\n const alreadyStarted = this.delayCount > 0\n this.delayCount = Math.floor(this.closeDelay / this.checkFrequency)\n return alreadyStarted\n })\n if (!alreadyStarted) {\n while (this.delayCount > 0) {\n await this.delayedCloseMutex.runExclusive(async () => {\n if (this.connections > 0 || !this.connected) {\n // cancel close\n this.delayCount = 0\n } else if (this.delayCount === 1) {\n // out of delay, close it\n await this.close()\n this.delayCount = 0\n } else {\n this.delayCount -= 1\n }\n await delay(this.checkFrequency)\n })\n }\n }\n }\n\n private async close() {\n return await this.connectionMutex.runExclusive(async () => {\n assertEx(this.connected, () => 'Unexpected close')\n this.connected = false\n await this.client.close(true)\n MongoClientWrapper.clients.delete(this.uri)\n })\n }\n}\n"],"mappings":"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","names":["assertEx","alreadyStarted","assertEx"]}
1
+ {"version":3,"sources":["../../src/Base.ts","../../src/Wrapper.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type {\n BulkWriteOptions,\n Collection,\n DeleteResult,\n Document,\n Filter,\n FindCursor,\n InsertOneOptions,\n MongoClient,\n OptionalUnlessRequiredId,\n UpdateFilter,\n WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\nexport class BaseMongoSdk<T extends Document> {\n config: BaseMongoSdkConfig\n\n constructor(config: BaseMongoSdkConfig) {\n this.config = config\n }\n\n get uri() {\n return (\n this.config.dbConnectionString\n // eslint-disable-next-line @stylistic/max-len\n ?? `mongodb+srv://${this.config.dbUserName}:${this.config.dbPassword}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n )\n }\n\n async deleteOne(filter: Filter<T>) {\n return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n return await collection.deleteOne(filter)\n })\n }\n\n async find(filter: Filter<T>) {\n return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n return collection.find(filter)\n })\n }\n\n async findOne(filter: Filter<T>) {\n return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n return await collection.findOne(filter)\n })\n }\n\n async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertMany(items, options)\n })\n }\n\n async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertOne(item, options)\n })\n }\n\n async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: false })\n })\n }\n\n async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: true })\n })\n }\n\n async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n return await this.useMongo<R>(async (client: MongoClient) => {\n return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n })\n }\n\n async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n const connection = await wrapper.connect()\n assertEx(connection, () => 'Connection failed')\n try {\n return await func(connection)\n } finally {\n await wrapper.disconnect()\n }\n }\n}\n","import type { MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\n\nexport class MongoClientWrapper {\n static readonly clients = new Map<string, MongoClientWrapper>()\n\n private client: MongoClient\n\n constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {\n const options: MongoClientOptions = { maxPoolSize, maxIdleTimeMS: closeDelay }\n this.client = new MongoClient(uri, options)\n }\n\n static get(uri: string, poolSize?: number, closeDelay?: number) {\n let client = this.clients.get(uri)\n if (!client) {\n client = new MongoClientWrapper(uri, poolSize, closeDelay)\n this.clients.set(uri, client)\n }\n return client\n }\n\n async connect() {\n return await Promise.resolve(this.client)\n }\n\n async disconnect() {\n return await Promise.resolve(0)\n }\n\n async initiateClose() {\n await Promise.resolve()\n }\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;;;ACCzB,SAAS,mBAAmB;AAErB,IAAM,qBAAN,MAAM,oBAAmB;AAAA,EAC9B,OAAgB,UAAU,oBAAI,IAAgC;AAAA,EAEtD;AAAA,EAER,YAAY,KAAa,aAAsB,YAAqB;AAClE,UAAM,UAA8B,EAAE,aAAa,eAAe,WAAW;AAC7E,SAAK,SAAS,IAAI,YAAY,KAAK,OAAO;AAAA,EAC5C;AAAA,EAEA,OAAO,IAAI,KAAa,UAAmB,YAAqB;AAC9D,QAAI,SAAS,KAAK,QAAQ,IAAI,GAAG;AACjC,QAAI,CAAC,QAAQ;AACX,eAAS,IAAI,oBAAmB,KAAK,UAAU,UAAU;AACzD,WAAK,QAAQ,IAAI,KAAK,MAAM;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU;AACd,WAAO,MAAM,QAAQ,QAAQ,KAAK,MAAM;AAAA,EAC1C;AAAA,EAEA,MAAM,aAAa;AACjB,WAAO,MAAM,QAAQ,QAAQ,CAAC;AAAA,EAChC;AAAA,EAEA,MAAM,gBAAgB;AACpB,UAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;;;ADfO,IAAM,eAAN,MAAuC;AAAA,EAC5C;AAAA,EAEA,YAAY,QAA4B;AACtC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,IAAI,MAAM;AACR,WACE,KAAK,OAAO,sBAET,iBAAiB,KAAK,OAAO,UAAU,IAAI,KAAK,OAAO,UAAU,IAAI,KAAK,OAAO,QAAQ,gBAAgB,KAAK,OAAO,MAAM;AAAA,EAElI;AAAA,EAEA,MAAM,UAAU,QAAmB;AACjC,WAAO,MAAM,KAAK,cAA4B,OAAO,eAA8B;AACjF,aAAO,MAAM,WAAW,UAAU,MAAM;AAAA,IAC1C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,KAAK,QAAmB;AAC5B,WAAO,MAAM,KAAK,cAAqC,CAAC,eAA8B;AACpF,aAAO,WAAW,KAAK,MAAM;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ,QAAmB;AAC/B,WAAO,MAAM,KAAK,cAAgC,OAAO,eAA8B;AACrF,aAAO,MAAM,WAAW,QAAQ,MAAM;AAAA,IACxC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,WAAW,OAAsC,SAA4B;AACjF,WAAO,MAAM,KAAK,cAAc,OAAO,eAA8B;AACnE,aAAO,MAAM,WAAW,WAAW,OAAO,OAAO;AAAA,IACnD,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,MAAmC,SAA4B;AAC7E,WAAO,MAAM,KAAK,cAAc,OAAO,eAA8B;AACnE,aAAO,MAAM,WAAW,UAAU,MAAM,OAAO;AAAA,IACjD,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,QAAmB,QAAyB;AAC1D,WAAO,MAAM,KAAK,cAAc,OAAO,eAA8B;AACnE,aAAO,MAAM,WAAW,UAAU,QAAQ,QAAQ,EAAE,QAAQ,MAAM,CAAC;AAAA,IACrE,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,QAAmB,QAAyB;AAC1D,WAAO,MAAM,KAAK,cAAc,OAAO,eAA8B;AACnE,aAAO,MAAM,WAAW,UAAU,QAAQ,QAAQ,EAAE,QAAQ,KAAK,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cAAiB,MAAqD;AAC1E,WAAO,MAAM,KAAK,SAAY,OAAO,WAAwB;AAC3D,aAAO,MAAM,KAAK,OAAO,GAAG,KAAK,OAAO,MAAM,EAAE,WAAc,KAAK,OAAO,UAAU,CAAC;AAAA,IACvF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAY,MAA+C;AAC/D,UAAM,UAAU,mBAAmB,IAAI,KAAK,KAAK,KAAK,OAAO,aAAa,KAAK,OAAO,UAAU;AAChG,UAAM,aAAa,MAAM,QAAQ,QAAQ;AACzC,aAAS,YAAY,MAAM,mBAAmB;AAC9C,QAAI;AACF,aAAO,MAAM,KAAK,UAAU;AAAA,IAC9B,UAAE;AACA,YAAM,QAAQ,WAAW;AAAA,IAC3B;AAAA,EACF;AACF;","names":[]}
@@ -1,27 +1,14 @@
1
1
  // src/Base.ts
2
- import { assertEx as assertEx2 } from "@xylabs/assert";
2
+ import { assertEx } from "@xylabs/assert";
3
3
 
4
4
  // src/Wrapper.ts
5
- import { assertEx } from "@xylabs/assert";
6
- import { delay } from "@xylabs/delay";
7
- import { forget } from "@xylabs/forget";
8
- import { Mutex } from "async-mutex";
9
5
  import { MongoClient } from "mongodb";
10
6
  var MongoClientWrapper = class _MongoClientWrapper {
11
7
  static clients = /* @__PURE__ */ new Map();
12
- checkFrequency = 100;
13
8
  client;
14
- closeDelay;
15
- connected = false;
16
- connectionMutex = new Mutex();
17
- connections = 0;
18
- delayCount = 0;
19
- delayedCloseMutex = new Mutex();
20
- uri;
21
9
  constructor(uri, maxPoolSize, closeDelay) {
22
- this.uri = uri;
23
- this.client = new MongoClient(uri, { maxPoolSize });
24
- this.closeDelay = closeDelay ?? 10 * 1e3;
10
+ const options = { maxPoolSize, maxIdleTimeMS: closeDelay };
11
+ this.client = new MongoClient(uri, options);
25
12
  }
26
13
  static get(uri, poolSize, closeDelay) {
27
14
  let client = this.clients.get(uri);
@@ -32,54 +19,13 @@ var MongoClientWrapper = class _MongoClientWrapper {
32
19
  return client;
33
20
  }
34
21
  async connect() {
35
- return await this.connectionMutex.runExclusive(async () => {
36
- if (this.connections === 0 && !this.connected) {
37
- await this.client.connect();
38
- this.connected = true;
39
- }
40
- this.connections += 1;
41
- return this.client;
42
- });
22
+ return await Promise.resolve(this.client);
43
23
  }
44
24
  async disconnect() {
45
- return await this.connectionMutex.runExclusive(() => {
46
- assertEx(this.connections > 0, () => "Unexpected disconnect");
47
- this.connections -= 1;
48
- if (this.connections === 0) {
49
- forget(this.initiateClose());
50
- }
51
- return this.connections;
52
- });
25
+ return await Promise.resolve(0);
53
26
  }
54
27
  async initiateClose() {
55
- const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {
56
- const alreadyStarted2 = this.delayCount > 0;
57
- this.delayCount = Math.floor(this.closeDelay / this.checkFrequency);
58
- return alreadyStarted2;
59
- });
60
- if (!alreadyStarted) {
61
- while (this.delayCount > 0) {
62
- await this.delayedCloseMutex.runExclusive(async () => {
63
- if (this.connections > 0 || !this.connected) {
64
- this.delayCount = 0;
65
- } else if (this.delayCount === 1) {
66
- await this.close();
67
- this.delayCount = 0;
68
- } else {
69
- this.delayCount -= 1;
70
- }
71
- await delay(this.checkFrequency);
72
- });
73
- }
74
- }
75
- }
76
- async close() {
77
- return await this.connectionMutex.runExclusive(async () => {
78
- assertEx(this.connected, () => "Unexpected close");
79
- this.connected = false;
80
- await this.client.close(true);
81
- _MongoClientWrapper.clients.delete(this.uri);
82
- });
28
+ await Promise.resolve();
83
29
  }
84
30
  };
85
31
 
@@ -135,7 +81,7 @@ var BaseMongoSdk = class {
135
81
  async useMongo(func) {
136
82
  const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay);
137
83
  const connection = await wrapper.connect();
138
- assertEx2(connection, () => "Connection failed");
84
+ assertEx(connection, () => "Connection failed");
139
85
  try {
140
86
  return await func(connection);
141
87
  } finally {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Base.ts","../../src/Wrapper.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type {\n BulkWriteOptions,\n Collection,\n DeleteResult,\n Document,\n Filter,\n FindCursor,\n InsertOneOptions,\n MongoClient,\n OptionalUnlessRequiredId,\n UpdateFilter,\n WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\nexport class BaseMongoSdk<T extends Document> {\n config: BaseMongoSdkConfig\n\n constructor(config: BaseMongoSdkConfig) {\n this.config = config\n }\n\n get uri() {\n return (\n this.config.dbConnectionString\n // eslint-disable-next-line @stylistic/max-len\n ?? `mongodb+srv://${this.config.dbUserName}:${this.config.dbPassword}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n )\n }\n\n async deleteOne(filter: Filter<T>) {\n return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n return await collection.deleteOne(filter)\n })\n }\n\n async find(filter: Filter<T>) {\n return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n return collection.find(filter)\n })\n }\n\n async findOne(filter: Filter<T>) {\n return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n return await collection.findOne(filter)\n })\n }\n\n async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertMany(items, options)\n })\n }\n\n async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertOne(item, options)\n })\n }\n\n async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: false })\n })\n }\n\n async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: true })\n })\n }\n\n async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n return await this.useMongo<R>(async (client: MongoClient) => {\n return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n })\n }\n\n async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n const connection = await wrapper.connect()\n assertEx(connection, () => 'Connection failed')\n try {\n return await func(connection)\n } finally {\n await wrapper.disconnect()\n }\n }\n}\n","import { assertEx } from '@xylabs/assert'\nimport { delay } from '@xylabs/delay'\nimport { forget } from '@xylabs/forget'\nimport { Mutex } from 'async-mutex'\nimport type { MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\n\nexport class MongoClientWrapper {\n static readonly clients = new Map<string, MongoClientWrapper>()\n\n private checkFrequency = 100\n private client: MongoClient\n private closeDelay\n private connected = false\n private connectionMutex = new Mutex()\n private connections = 0\n private delayCount = 0\n private delayedCloseMutex = new Mutex()\n\n private uri: string\n\n constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {\n this.uri = uri\n this.client = new MongoClient(uri, { maxPoolSize } as MongoClientOptions)\n this.closeDelay = closeDelay ?? 10 * 1000 /* 10 seconds default */\n }\n\n static get(uri: string, poolSize?: number, closeDelay?: number) {\n let client = this.clients.get(uri)\n if (!client) {\n client = new MongoClientWrapper(uri, poolSize, closeDelay)\n this.clients.set(uri, client)\n }\n return client\n }\n\n async connect() {\n return await this.connectionMutex.runExclusive<MongoClient>(async () => {\n if (this.connections === 0 && !this.connected) {\n await this.client.connect()\n this.connected = true\n }\n this.connections += 1\n return this.client\n })\n }\n\n async disconnect() {\n return await this.connectionMutex.runExclusive(() => {\n assertEx(this.connections > 0, () => 'Unexpected disconnect')\n this.connections -= 1\n if (this.connections === 0) {\n forget(this.initiateClose())\n }\n return this.connections\n })\n }\n\n async initiateClose() {\n const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {\n const alreadyStarted = this.delayCount > 0\n this.delayCount = Math.floor(this.closeDelay / this.checkFrequency)\n return alreadyStarted\n })\n if (!alreadyStarted) {\n while (this.delayCount > 0) {\n await this.delayedCloseMutex.runExclusive(async () => {\n if (this.connections > 0 || !this.connected) {\n // cancel close\n this.delayCount = 0\n } else if (this.delayCount === 1) {\n // out of delay, close it\n await this.close()\n this.delayCount = 0\n } else {\n this.delayCount -= 1\n }\n await delay(this.checkFrequency)\n })\n }\n }\n }\n\n private async close() {\n return await this.connectionMutex.runExclusive(async () => {\n assertEx(this.connected, () => 'Unexpected close')\n this.connected = false\n await this.client.close(true)\n MongoClientWrapper.clients.delete(this.uri)\n })\n }\n}\n"],"mappings":"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","names":["assertEx","alreadyStarted","assertEx"]}
1
+ {"version":3,"sources":["../../src/Base.ts","../../src/Wrapper.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type {\n BulkWriteOptions,\n Collection,\n DeleteResult,\n Document,\n Filter,\n FindCursor,\n InsertOneOptions,\n MongoClient,\n OptionalUnlessRequiredId,\n UpdateFilter,\n WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\nexport class BaseMongoSdk<T extends Document> {\n config: BaseMongoSdkConfig\n\n constructor(config: BaseMongoSdkConfig) {\n this.config = config\n }\n\n get uri() {\n return (\n this.config.dbConnectionString\n // eslint-disable-next-line @stylistic/max-len\n ?? `mongodb+srv://${this.config.dbUserName}:${this.config.dbPassword}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n )\n }\n\n async deleteOne(filter: Filter<T>) {\n return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n return await collection.deleteOne(filter)\n })\n }\n\n async find(filter: Filter<T>) {\n return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n return collection.find(filter)\n })\n }\n\n async findOne(filter: Filter<T>) {\n return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n return await collection.findOne(filter)\n })\n }\n\n async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertMany(items, options)\n })\n }\n\n async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertOne(item, options)\n })\n }\n\n async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: false })\n })\n }\n\n async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: true })\n })\n }\n\n async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n return await this.useMongo<R>(async (client: MongoClient) => {\n return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n })\n }\n\n async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n const connection = await wrapper.connect()\n assertEx(connection, () => 'Connection failed')\n try {\n return await func(connection)\n } finally {\n await wrapper.disconnect()\n }\n }\n}\n","import type { MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\n\nexport class MongoClientWrapper {\n static readonly clients = new Map<string, MongoClientWrapper>()\n\n private client: MongoClient\n\n constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {\n const options: MongoClientOptions = { maxPoolSize, maxIdleTimeMS: closeDelay }\n this.client = new MongoClient(uri, options)\n }\n\n static get(uri: string, poolSize?: number, closeDelay?: number) {\n let client = this.clients.get(uri)\n if (!client) {\n client = new MongoClientWrapper(uri, poolSize, closeDelay)\n this.clients.set(uri, client)\n }\n return client\n }\n\n async connect() {\n return await Promise.resolve(this.client)\n }\n\n async disconnect() {\n return await Promise.resolve(0)\n }\n\n async initiateClose() {\n await Promise.resolve()\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,27 +1,14 @@
1
1
  // src/Base.ts
2
- import { assertEx as assertEx2 } from "@xylabs/assert";
2
+ import { assertEx } from "@xylabs/assert";
3
3
 
4
4
  // src/Wrapper.ts
5
- import { assertEx } from "@xylabs/assert";
6
- import { delay } from "@xylabs/delay";
7
- import { forget } from "@xylabs/forget";
8
- import { Mutex } from "async-mutex";
9
5
  import { MongoClient } from "mongodb";
10
6
  var MongoClientWrapper = class _MongoClientWrapper {
11
7
  static clients = /* @__PURE__ */ new Map();
12
- checkFrequency = 100;
13
8
  client;
14
- closeDelay;
15
- connected = false;
16
- connectionMutex = new Mutex();
17
- connections = 0;
18
- delayCount = 0;
19
- delayedCloseMutex = new Mutex();
20
- uri;
21
9
  constructor(uri, maxPoolSize, closeDelay) {
22
- this.uri = uri;
23
- this.client = new MongoClient(uri, { maxPoolSize });
24
- this.closeDelay = closeDelay ?? 10 * 1e3;
10
+ const options = { maxPoolSize, maxIdleTimeMS: closeDelay };
11
+ this.client = new MongoClient(uri, options);
25
12
  }
26
13
  static get(uri, poolSize, closeDelay) {
27
14
  let client = this.clients.get(uri);
@@ -32,54 +19,13 @@ var MongoClientWrapper = class _MongoClientWrapper {
32
19
  return client;
33
20
  }
34
21
  async connect() {
35
- return await this.connectionMutex.runExclusive(async () => {
36
- if (this.connections === 0 && !this.connected) {
37
- await this.client.connect();
38
- this.connected = true;
39
- }
40
- this.connections += 1;
41
- return this.client;
42
- });
22
+ return await Promise.resolve(this.client);
43
23
  }
44
24
  async disconnect() {
45
- return await this.connectionMutex.runExclusive(() => {
46
- assertEx(this.connections > 0, () => "Unexpected disconnect");
47
- this.connections -= 1;
48
- if (this.connections === 0) {
49
- forget(this.initiateClose());
50
- }
51
- return this.connections;
52
- });
25
+ return await Promise.resolve(0);
53
26
  }
54
27
  async initiateClose() {
55
- const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {
56
- const alreadyStarted2 = this.delayCount > 0;
57
- this.delayCount = Math.floor(this.closeDelay / this.checkFrequency);
58
- return alreadyStarted2;
59
- });
60
- if (!alreadyStarted) {
61
- while (this.delayCount > 0) {
62
- await this.delayedCloseMutex.runExclusive(async () => {
63
- if (this.connections > 0 || !this.connected) {
64
- this.delayCount = 0;
65
- } else if (this.delayCount === 1) {
66
- await this.close();
67
- this.delayCount = 0;
68
- } else {
69
- this.delayCount -= 1;
70
- }
71
- await delay(this.checkFrequency);
72
- });
73
- }
74
- }
75
- }
76
- async close() {
77
- return await this.connectionMutex.runExclusive(async () => {
78
- assertEx(this.connected, () => "Unexpected close");
79
- this.connected = false;
80
- await this.client.close(true);
81
- _MongoClientWrapper.clients.delete(this.uri);
82
- });
28
+ await Promise.resolve();
83
29
  }
84
30
  };
85
31
 
@@ -135,7 +81,7 @@ var BaseMongoSdk = class {
135
81
  async useMongo(func) {
136
82
  const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay);
137
83
  const connection = await wrapper.connect();
138
- assertEx2(connection, () => "Connection failed");
84
+ assertEx(connection, () => "Connection failed");
139
85
  try {
140
86
  return await func(connection);
141
87
  } finally {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Base.ts","../../src/Wrapper.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type {\n BulkWriteOptions,\n Collection,\n DeleteResult,\n Document,\n Filter,\n FindCursor,\n InsertOneOptions,\n MongoClient,\n OptionalUnlessRequiredId,\n UpdateFilter,\n WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\nexport class BaseMongoSdk<T extends Document> {\n config: BaseMongoSdkConfig\n\n constructor(config: BaseMongoSdkConfig) {\n this.config = config\n }\n\n get uri() {\n return (\n this.config.dbConnectionString\n // eslint-disable-next-line @stylistic/max-len\n ?? `mongodb+srv://${this.config.dbUserName}:${this.config.dbPassword}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n )\n }\n\n async deleteOne(filter: Filter<T>) {\n return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n return await collection.deleteOne(filter)\n })\n }\n\n async find(filter: Filter<T>) {\n return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n return collection.find(filter)\n })\n }\n\n async findOne(filter: Filter<T>) {\n return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n return await collection.findOne(filter)\n })\n }\n\n async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertMany(items, options)\n })\n }\n\n async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertOne(item, options)\n })\n }\n\n async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: false })\n })\n }\n\n async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: true })\n })\n }\n\n async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n return await this.useMongo<R>(async (client: MongoClient) => {\n return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n })\n }\n\n async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n const connection = await wrapper.connect()\n assertEx(connection, () => 'Connection failed')\n try {\n return await func(connection)\n } finally {\n await wrapper.disconnect()\n }\n }\n}\n","import { assertEx } from '@xylabs/assert'\nimport { delay } from '@xylabs/delay'\nimport { forget } from '@xylabs/forget'\nimport { Mutex } from 'async-mutex'\nimport type { MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\n\nexport class MongoClientWrapper {\n static readonly clients = new Map<string, MongoClientWrapper>()\n\n private checkFrequency = 100\n private client: MongoClient\n private closeDelay\n private connected = false\n private connectionMutex = new Mutex()\n private connections = 0\n private delayCount = 0\n private delayedCloseMutex = new Mutex()\n\n private uri: string\n\n constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {\n this.uri = uri\n this.client = new MongoClient(uri, { maxPoolSize } as MongoClientOptions)\n this.closeDelay = closeDelay ?? 10 * 1000 /* 10 seconds default */\n }\n\n static get(uri: string, poolSize?: number, closeDelay?: number) {\n let client = this.clients.get(uri)\n if (!client) {\n client = new MongoClientWrapper(uri, poolSize, closeDelay)\n this.clients.set(uri, client)\n }\n return client\n }\n\n async connect() {\n return await this.connectionMutex.runExclusive<MongoClient>(async () => {\n if (this.connections === 0 && !this.connected) {\n await this.client.connect()\n this.connected = true\n }\n this.connections += 1\n return this.client\n })\n }\n\n async disconnect() {\n return await this.connectionMutex.runExclusive(() => {\n assertEx(this.connections > 0, () => 'Unexpected disconnect')\n this.connections -= 1\n if (this.connections === 0) {\n forget(this.initiateClose())\n }\n return this.connections\n })\n }\n\n async initiateClose() {\n const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {\n const alreadyStarted = this.delayCount > 0\n this.delayCount = Math.floor(this.closeDelay / this.checkFrequency)\n return alreadyStarted\n })\n if (!alreadyStarted) {\n while (this.delayCount > 0) {\n await this.delayedCloseMutex.runExclusive(async () => {\n if (this.connections > 0 || !this.connected) {\n // cancel close\n this.delayCount = 0\n } else if (this.delayCount === 1) {\n // out of delay, close it\n await this.close()\n this.delayCount = 0\n } else {\n this.delayCount -= 1\n }\n await delay(this.checkFrequency)\n })\n }\n }\n }\n\n private async close() {\n return await this.connectionMutex.runExclusive(async () => {\n assertEx(this.connected, () => 'Unexpected close')\n this.connected = false\n await this.client.close(true)\n MongoClientWrapper.clients.delete(this.uri)\n })\n }\n}\n"],"mappings":"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","names":["assertEx","alreadyStarted","assertEx"]}
1
+ {"version":3,"sources":["../../src/Base.ts","../../src/Wrapper.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type {\n BulkWriteOptions,\n Collection,\n DeleteResult,\n Document,\n Filter,\n FindCursor,\n InsertOneOptions,\n MongoClient,\n OptionalUnlessRequiredId,\n UpdateFilter,\n WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\nexport class BaseMongoSdk<T extends Document> {\n config: BaseMongoSdkConfig\n\n constructor(config: BaseMongoSdkConfig) {\n this.config = config\n }\n\n get uri() {\n return (\n this.config.dbConnectionString\n // eslint-disable-next-line @stylistic/max-len\n ?? `mongodb+srv://${this.config.dbUserName}:${this.config.dbPassword}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n )\n }\n\n async deleteOne(filter: Filter<T>) {\n return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n return await collection.deleteOne(filter)\n })\n }\n\n async find(filter: Filter<T>) {\n return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n return collection.find(filter)\n })\n }\n\n async findOne(filter: Filter<T>) {\n return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n return await collection.findOne(filter)\n })\n }\n\n async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertMany(items, options)\n })\n }\n\n async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.insertOne(item, options)\n })\n }\n\n async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: false })\n })\n }\n\n async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n return await this.useCollection(async (collection: Collection<T>) => {\n return await collection.updateOne(filter, fields, { upsert: true })\n })\n }\n\n async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n return await this.useMongo<R>(async (client: MongoClient) => {\n return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n })\n }\n\n async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n const connection = await wrapper.connect()\n assertEx(connection, () => 'Connection failed')\n try {\n return await func(connection)\n } finally {\n await wrapper.disconnect()\n }\n }\n}\n","import type { MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\n\nexport class MongoClientWrapper {\n static readonly clients = new Map<string, MongoClientWrapper>()\n\n private client: MongoClient\n\n constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {\n const options: MongoClientOptions = { maxPoolSize, maxIdleTimeMS: closeDelay }\n this.client = new MongoClient(uri, options)\n }\n\n static get(uri: string, poolSize?: number, closeDelay?: number) {\n let client = this.clients.get(uri)\n if (!client) {\n client = new MongoClientWrapper(uri, poolSize, closeDelay)\n this.clients.set(uri, client)\n }\n return client\n }\n\n async connect() {\n return await Promise.resolve(this.client)\n }\n\n async disconnect() {\n return await Promise.resolve(0)\n }\n\n async initiateClose() {\n await Promise.resolve()\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,20 +1,11 @@
1
1
  import { MongoClient } from 'mongodb';
2
2
  export declare class MongoClientWrapper {
3
3
  static readonly clients: Map<string, MongoClientWrapper>;
4
- private checkFrequency;
5
4
  private client;
6
- private closeDelay;
7
- private connected;
8
- private connectionMutex;
9
- private connections;
10
- private delayCount;
11
- private delayedCloseMutex;
12
- private uri;
13
5
  constructor(uri: string, maxPoolSize?: number, closeDelay?: number);
14
6
  static get(uri: string, poolSize?: number, closeDelay?: number): MongoClientWrapper;
15
7
  connect(): Promise<MongoClient>;
16
8
  disconnect(): Promise<number>;
17
9
  initiateClose(): Promise<void>;
18
- private close;
19
10
  }
20
11
  //# sourceMappingURL=Wrapper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Wrapper.d.ts","sourceRoot":"","sources":["../../src/Wrapper.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErC,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,QAAQ,CAAC,OAAO,kCAAwC;IAE/D,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAA;IAClB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,eAAe,CAAc;IACrC,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,iBAAiB,CAAc;IAEvC,OAAO,CAAC,GAAG,CAAQ;gBAEP,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IAMlE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IASxD,OAAO;IAWP,UAAU;IAWV,aAAa;YAyBL,KAAK;CAQpB"}
1
+ {"version":3,"file":"Wrapper.d.ts","sourceRoot":"","sources":["../../src/Wrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErC,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,QAAQ,CAAC,OAAO,kCAAwC;IAE/D,OAAO,CAAC,MAAM,CAAa;gBAEf,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IAKlE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;IASxD,OAAO;IAIP,UAAU;IAIV,aAAa;CAGpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/mongo",
3
- "version": "4.11.1",
3
+ "version": "4.11.2",
4
4
  "description": "Base functionality used throughout XYO TypeScript/JavaScript libraries that access Mongo DB",
5
5
  "keywords": [
6
6
  "mongo",
@@ -40,10 +40,7 @@
40
40
  "module": "dist/node/index.mjs",
41
41
  "types": "dist/types/index.d.ts",
42
42
  "dependencies": {
43
- "@xylabs/assert": "^4.11.1",
44
- "@xylabs/delay": "^4.11.1",
45
- "@xylabs/forget": "^4.11.1",
46
- "async-mutex": "^0.5.0",
43
+ "@xylabs/assert": "^4.11.2",
47
44
  "mongodb": "~6.16.0"
48
45
  },
49
46
  "devDependencies": {
package/src/Wrapper.ts CHANGED
@@ -1,28 +1,14 @@
1
- import { assertEx } from '@xylabs/assert'
2
- import { delay } from '@xylabs/delay'
3
- import { forget } from '@xylabs/forget'
4
- import { Mutex } from 'async-mutex'
5
1
  import type { MongoClientOptions } from 'mongodb'
6
2
  import { MongoClient } from 'mongodb'
7
3
 
8
4
  export class MongoClientWrapper {
9
5
  static readonly clients = new Map<string, MongoClientWrapper>()
10
6
 
11
- private checkFrequency = 100
12
7
  private client: MongoClient
13
- private closeDelay
14
- private connected = false
15
- private connectionMutex = new Mutex()
16
- private connections = 0
17
- private delayCount = 0
18
- private delayedCloseMutex = new Mutex()
19
-
20
- private uri: string
21
8
 
22
9
  constructor(uri: string, maxPoolSize?: number, closeDelay?: number) {
23
- this.uri = uri
24
- this.client = new MongoClient(uri, { maxPoolSize } as MongoClientOptions)
25
- this.closeDelay = closeDelay ?? 10 * 1000 /* 10 seconds default */
10
+ const options: MongoClientOptions = { maxPoolSize, maxIdleTimeMS: closeDelay }
11
+ this.client = new MongoClient(uri, options)
26
12
  }
27
13
 
28
14
  static get(uri: string, poolSize?: number, closeDelay?: number) {
@@ -35,58 +21,14 @@ export class MongoClientWrapper {
35
21
  }
36
22
 
37
23
  async connect() {
38
- return await this.connectionMutex.runExclusive<MongoClient>(async () => {
39
- if (this.connections === 0 && !this.connected) {
40
- await this.client.connect()
41
- this.connected = true
42
- }
43
- this.connections += 1
44
- return this.client
45
- })
24
+ return await Promise.resolve(this.client)
46
25
  }
47
26
 
48
27
  async disconnect() {
49
- return await this.connectionMutex.runExclusive(() => {
50
- assertEx(this.connections > 0, () => 'Unexpected disconnect')
51
- this.connections -= 1
52
- if (this.connections === 0) {
53
- forget(this.initiateClose())
54
- }
55
- return this.connections
56
- })
28
+ return await Promise.resolve(0)
57
29
  }
58
30
 
59
31
  async initiateClose() {
60
- const alreadyStarted = await this.delayedCloseMutex.runExclusive(() => {
61
- const alreadyStarted = this.delayCount > 0
62
- this.delayCount = Math.floor(this.closeDelay / this.checkFrequency)
63
- return alreadyStarted
64
- })
65
- if (!alreadyStarted) {
66
- while (this.delayCount > 0) {
67
- await this.delayedCloseMutex.runExclusive(async () => {
68
- if (this.connections > 0 || !this.connected) {
69
- // cancel close
70
- this.delayCount = 0
71
- } else if (this.delayCount === 1) {
72
- // out of delay, close it
73
- await this.close()
74
- this.delayCount = 0
75
- } else {
76
- this.delayCount -= 1
77
- }
78
- await delay(this.checkFrequency)
79
- })
80
- }
81
- }
82
- }
83
-
84
- private async close() {
85
- return await this.connectionMutex.runExclusive(async () => {
86
- assertEx(this.connected, () => 'Unexpected close')
87
- this.connected = false
88
- await this.client.close(true)
89
- MongoClientWrapper.clients.delete(this.uri)
90
- })
32
+ await Promise.resolve()
91
33
  }
92
34
  }