underpost 2.8.884 → 2.8.885

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.
Files changed (43) hide show
  1. package/README.md +4 -120
  2. package/bin/deploy.js +9 -10
  3. package/bin/file.js +4 -6
  4. package/cli.md +15 -11
  5. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  6. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  7. package/package.json +1 -1
  8. package/src/cli/cluster.js +21 -0
  9. package/src/cli/cron.js +8 -0
  10. package/src/cli/db.js +63 -1
  11. package/src/cli/deploy.js +156 -3
  12. package/src/cli/env.js +43 -0
  13. package/src/cli/fs.js +94 -0
  14. package/src/cli/image.js +8 -0
  15. package/src/cli/index.js +17 -4
  16. package/src/cli/monitor.js +0 -1
  17. package/src/cli/repository.js +95 -2
  18. package/src/client/components/core/Css.js +16 -0
  19. package/src/client/components/core/Docs.js +5 -13
  20. package/src/client/components/core/Modal.js +48 -29
  21. package/src/client/components/core/Router.js +6 -3
  22. package/src/client/components/core/Worker.js +205 -118
  23. package/src/client/components/default/MenuDefault.js +1 -0
  24. package/src/client.dev.js +6 -3
  25. package/src/db/DataBaseProvider.js +65 -12
  26. package/src/db/mariadb/MariaDB.js +39 -6
  27. package/src/db/mongo/MongooseDB.js +51 -133
  28. package/src/index.js +1 -1
  29. package/src/mailer/EmailRender.js +58 -9
  30. package/src/mailer/MailerProvider.js +98 -25
  31. package/src/runtime/express/Express.js +20 -34
  32. package/src/server/auth.js +9 -28
  33. package/src/server/client-build-live.js +14 -5
  34. package/src/server/client-dev-server.js +21 -8
  35. package/src/server/conf.js +78 -25
  36. package/src/server/peer.js +2 -2
  37. package/src/server/runtime.js +0 -5
  38. package/src/server/start.js +39 -0
  39. package/src/ws/IoInterface.js +132 -39
  40. package/src/ws/IoServer.js +79 -31
  41. package/src/ws/core/core.ws.connection.js +50 -16
  42. package/src/ws/core/core.ws.emit.js +47 -8
  43. package/src/ws/core/core.ws.server.js +62 -10
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Utility class for managing Progressive Web App (PWA) worker functionalities,
3
+ * including service worker registration, caching, and notification management.
4
+ * This class is designed to be used as a singleton instance (exported as 'Worker').
5
+ * @module src/client/components/core/Worker.js
6
+ * @namespace PwaWorker
7
+ */
8
+
1
9
  import { BtnIcon } from './BtnIcon.js';
2
10
  import { s4 } from './CommonJs.js';
3
11
  import { EventsUI } from './EventsUI.js';
@@ -9,11 +17,64 @@ import { s } from './VanillaJs.js';
9
17
  import { getProxyPath } from './Router.js';
10
18
  const logger = loggerFactory(import.meta);
11
19
 
12
- const Worker = {
13
- devMode: () => location.origin.match('localhost') || location.origin.match('127.0.0.1'),
14
- instance: async function ({ router, render }) {
15
- Worker.title = `${s('title').textContent}`;
16
- // logger.warn('Init worker', Worker.title);
20
+ /**
21
+ * Manages the PWA lifecycle, service workers, and related client-side events.
22
+ * @memberof PwaWorker
23
+ */
24
+ class PwaWorker {
25
+ /**
26
+ * The application title, usually from the <title> tag content.
27
+ * @type {string}
28
+ */
29
+ title = '';
30
+
31
+ /**
32
+ * Tracks if notification permission has been granted and is active.
33
+ * @type {boolean}
34
+ */
35
+ notificationActive = false;
36
+
37
+ /**
38
+ * A function reference to the service worker's update method (registration.update()),
39
+ * dynamically set upon successful registration status check.
40
+ * @type {function(): Promise<void>}
41
+ */
42
+ updateServiceWorker = async () => {};
43
+
44
+ /**
45
+ * Router instance reference, initialized during the `instance` call.
46
+ * @type {object | null}
47
+ */
48
+ RouterInstance = null;
49
+
50
+ /**
51
+ * Creates an instance of PwaWorker and initializes the application title.
52
+ * @memberof PwaWorker
53
+ */
54
+ constructor() {
55
+ this.title = `${s('title').textContent}`;
56
+ }
57
+
58
+ /**
59
+ * Checks if the application is running in development mode (localhost or 127.0.0.1).
60
+ * @memberof PwaWorker
61
+ * @returns {boolean} True if in development mode.
62
+ */
63
+ devMode() {
64
+ return location.origin.match('localhost') || location.origin.match('127.0.0.1');
65
+ }
66
+
67
+ /**
68
+ * Initializes the PWA worker, registers global/worker event listeners,
69
+ * checks service worker status, and renders the initial content.
70
+ * This is the main entry point for the worker setup.
71
+ * @memberof PwaWorker
72
+ * @param {object} options - Configuration options.
73
+ * @param {function(): object} options.router - Function to get the router instance.
74
+ * @param {function(): Promise<void>} options.render - Function to render the application's UI.
75
+ * @returns {Promise<void>}
76
+ */
77
+ async instance({ router, render }) {
17
78
  window.ononline = async () => {
18
79
  logger.warn('ononline');
19
80
  };
@@ -23,13 +84,15 @@ const Worker = {
23
84
  setTimeout(() => {
24
85
  if ('onLine' in navigator && navigator.onLine) window.ononline();
25
86
  });
87
+
26
88
  if ('serviceWorker' in navigator) {
27
89
  navigator.serviceWorker.addEventListener('controllerchange', () => {
28
90
  logger.info('The controller of current browsing context has changed.');
29
91
  });
30
92
  navigator.serviceWorker.ready.then((worker) => {
31
- logger.info('Ready', worker);
32
- // event message
93
+ logger.info('Service Worker Ready', worker);
94
+
95
+ // event message listener
33
96
  navigator.serviceWorker.addEventListener('message', (event) => {
34
97
  logger.info('Received event message', event.data);
35
98
  const { status } = event.data;
@@ -68,36 +131,53 @@ const Worker = {
68
131
  LoadRouter(this.RouterInstance);
69
132
  LoadingAnimation.removeSplashScreen();
70
133
  if (this.devMode()) {
71
- const delayLiveReload = 1250;
72
- // Dev mode
73
-
74
- window.addEventListener('visibilitychange', (event) => {
75
- // if (document.visibilityState === 'visible') {
76
- // Worker.reload(delayLiveReload);
77
- // }
78
- });
79
- window.addEventListener('focus', function () {
80
- // Worker.reload(delayLiveReload);
81
- });
134
+ // const delayLiveReload = 1250;
135
+ // window.addEventListener('visibilitychange', () => {
136
+ // if (document.visibilityState === 'visible') {
137
+ // this.reload(delayLiveReload);
138
+ // }
139
+ // });
140
+ // window.addEventListener('focus', () => {
141
+ // this.reload(delayLiveReload);
142
+ // });
82
143
  }
83
144
  window.serviceWorkerReady = true;
84
- },
145
+ }
146
+
147
+ /**
148
+ * Gets the current service worker registration.
149
+ * @memberof PwaWorker
150
+ * @returns {Promise<ServiceWorkerRegistration | undefined>} The service worker registration object, or undefined.
151
+ */
152
+ async getRegistration() {
153
+ return navigator.serviceWorker.getRegistration();
154
+ }
85
155
 
86
- // Get the current service worker registration.
87
- getRegistration: async function () {
88
- return await navigator.serviceWorker.getRegistration();
89
- },
90
- reload: async function (timeOut = 3000) {
91
- return await new Promise((resolve) => {
92
- if (navigator.serviceWorker && navigator.serviceWorker.controller)
156
+ /**
157
+ * Forces the current service worker to skip waiting and reloads the page
158
+ * to apply the new service worker immediately.
159
+ * @memberof PwaWorker
160
+ * @param {number} [timeOut=3000] - Delay in milliseconds before reloading the page.
161
+ * @returns {Promise<void>} A promise that resolves after the page is reloaded.
162
+ */
163
+ async reload(timeOut = 3000) {
164
+ return new Promise((resolve) => {
165
+ if (navigator.serviceWorker && navigator.serviceWorker.controller) {
93
166
  navigator.serviceWorker.controller.postMessage({
94
167
  status: 'skipWaiting',
95
168
  });
96
- // (location.href = `${location.origin}${location.pathname}${location.search}`)
169
+ }
97
170
  setTimeout(() => resolve(location.reload()), timeOut);
98
171
  });
99
- },
100
- update: async function () {
172
+ }
173
+
174
+ /**
175
+ * Updates the application by clearing specific caches and running the service worker update logic.
176
+ * Cache names matching 'components/', 'services/', or '.index.js' are deleted.
177
+ * @memberof PwaWorker
178
+ * @returns {Promise<void>}
179
+ */
180
+ async update() {
101
181
  const isInstall = await this.status();
102
182
  if (isInstall) {
103
183
  const cacheNames = await caches.keys();
@@ -108,11 +188,17 @@ const Worker = {
108
188
  }
109
189
  await this.updateServiceWorker();
110
190
  }
111
- },
112
- updateServiceWorker: async function () {},
113
- status: function () {
191
+ }
192
+
193
+ /**
194
+ * Checks the current status of all service worker registrations and sets the
195
+ * `updateServiceWorker` function reference if an active worker is found.
196
+ * @memberof PwaWorker
197
+ * @returns {Promise<boolean>} True if at least one service worker is registered.
198
+ */
199
+ status() {
114
200
  let status = false;
115
- return new Promise((resolve, reject) => {
201
+ return new Promise((resolve) => {
116
202
  if ('serviceWorker' in navigator) {
117
203
  navigator.serviceWorker
118
204
  .getRegistrations()
@@ -122,81 +208,87 @@ const Worker = {
122
208
  else if (registration.waiting) logger.info('waiting', registration);
123
209
  else if (registration.active) {
124
210
  logger.info('active', registration);
211
+ // Dynamically set the update function
125
212
  this.updateServiceWorker = async () => await registration.update();
126
213
  }
127
214
  }
128
215
  if (registrations.length > 0) status = true;
216
+ resolve(status);
129
217
  })
130
218
  .catch((...args) => {
131
- logger.error(...args);
132
- return resolve(false);
133
- })
134
- .finally((...args) => {
135
- logger.info('Finally status', args);
136
- return resolve(status);
219
+ logger.error('Error getting service worker registrations:', ...args);
220
+ resolve(false);
137
221
  });
138
222
  } else {
139
- logger.warn('Disabled');
140
- return resolve(false);
223
+ logger.warn('Service Worker Disabled in browser');
224
+ resolve(false);
141
225
  }
142
226
  });
143
- },
144
- install: function () {
145
- return new Promise((resolve, reject) => {
227
+ }
228
+
229
+ /**
230
+ * Registers the service worker (`sw.js`) with the browser.
231
+ * @memberof PwaWorker
232
+ * @returns {Promise<Array<any>>} A promise that resolves with the registration arguments.
233
+ */
234
+ install() {
235
+ return new Promise((resolve) => {
146
236
  if ('serviceWorker' in navigator) {
147
237
  navigator.serviceWorker
148
238
  .register(`${getProxyPath()}sw.js`, {
149
- // scope: getProxyPath(),
150
- // scope: '/',
151
239
  type: 'module',
152
240
  })
153
241
  .then((...args) => {
154
- logger.warn('Already Registered', args);
242
+ logger.warn('Service Worker Registered', args);
243
+ resolve(args);
155
244
  })
156
245
  .catch((...args) => {
157
- logger.error(...args);
158
- return resolve(args);
159
- })
160
- .finally((...args) => {
161
- logger.info('Finally install', args);
162
- return resolve(args);
246
+ logger.error('Error registering service worker:', ...args);
247
+ resolve(args);
163
248
  });
164
249
  } else {
165
- logger.warn('Disabled');
166
- return resolve();
250
+ logger.warn('Service Worker Disabled in browser');
251
+ resolve([]);
167
252
  }
168
253
  });
169
- },
170
- uninstall: function () {
171
- return new Promise(async (resolve, reject) => {
254
+ }
255
+
256
+ /**
257
+ * Unregisters all service workers and deletes all application caches.
258
+ * @memberof PwaWorker
259
+ * @returns {Promise<Array<any>>} A promise that resolves after uninstallation.
260
+ */
261
+ uninstall() {
262
+ return new Promise(async (resolve) => {
172
263
  if ('serviceWorker' in navigator) {
173
- navigator.serviceWorker
174
- .getRegistrations()
175
- .then(async (registrations) => {
176
- const cacheNames = await caches.keys();
177
- for (const cacheName of cacheNames) await caches.delete(cacheName);
178
- for (const registration of registrations) {
179
- logger.info('remove', registration);
180
- registration.unregister();
181
- }
182
- })
183
- .catch((...args) => {
184
- logger.error(...args);
185
- return resolve(args);
186
- })
187
- .finally((...args) => {
188
- logger.info('Finally uninstall', args);
189
- return resolve(args);
190
- });
264
+ try {
265
+ const registrations = await navigator.serviceWorker.getRegistrations();
266
+ const cacheNames = await caches.keys();
267
+ for (const cacheName of cacheNames) await caches.delete(cacheName);
268
+ for (const registration of registrations) {
269
+ logger.info('Removing service worker registration', registration);
270
+ registration.unregister();
271
+ }
272
+ resolve([]);
273
+ } catch (error) {
274
+ logger.error('Error during service worker uninstallation:', error);
275
+ resolve([error]);
276
+ }
191
277
  } else {
192
- logger.warn('Disabled');
193
- return resolve();
278
+ logger.warn('Service Worker Disabled in browser');
279
+ resolve([]);
194
280
  }
195
281
  });
196
- },
197
- notificationActive: false,
198
- notificationRequestPermission: function () {
199
- return new Promise((resolve, reject) =>
282
+ }
283
+
284
+ /**
285
+ * Requests permission from the user to display notifications.
286
+ * Sets the internal `notificationActive` state.
287
+ * @memberof PwaWorker
288
+ * @returns {Promise<boolean>} True if permission is granted, false otherwise.
289
+ */
290
+ notificationRequestPermission() {
291
+ return new Promise((resolve) =>
200
292
  Notification.requestPermission().then((result) => {
201
293
  if (result === 'granted') {
202
294
  this.notificationActive = true;
@@ -207,8 +299,14 @@ const Worker = {
207
299
  }
208
300
  }),
209
301
  );
210
- },
211
- notificationShow: function () {
302
+ }
303
+
304
+ /**
305
+ * Shows a sample notification if permission is granted.
306
+ * @memberof PwaWorker
307
+ * @returns {void}
308
+ */
309
+ notificationShow() {
212
310
  Notification.requestPermission().then((result) => {
213
311
  if (result === 'granted') {
214
312
  navigator.serviceWorker.ready.then((registration) => {
@@ -217,18 +315,25 @@ const Worker = {
217
315
  icon: '../images/touch/chrome-touch-icon.png',
218
316
  vibrate: [200, 100, 200, 100, 200, 100, 200],
219
317
  tag: 'vibration-sample',
220
- requireInteraction: true, // boolean to manually close the notification
318
+ requireInteraction: true,
221
319
  });
222
320
  });
223
321
  }
224
322
  });
225
- },
226
- // TODO: GPS management
227
- RenderSetting: async function () {
323
+ }
324
+
325
+ /**
326
+ * Renders the UI for PWA settings, including buttons for cleaning cache and worker management.
327
+ * It also attaches the click handler for the 'clean-cache' button.
328
+ * @memberof PwaWorker
329
+ * @returns {Promise<string>} The HTML string for the settings section.
330
+ */
331
+ async RenderSetting() {
228
332
  setTimeout(() => {
333
+ // Event listener for the clean cache button
229
334
  EventsUI.onClick(`.btn-clean-cache`, async (e) => {
230
335
  e.preventDefault();
231
- // await this.update();
336
+ // Clear local storage, uninstall the worker, and reload
232
337
  localStorage.clear();
233
338
  await this.uninstall();
234
339
  await this.reload();
@@ -252,33 +357,15 @@ const Worker = {
252
357
  label: html`<i class="fas fa-sync-alt"></i> ${Translate.Render('Reload')}`,
253
358
  })}
254
359
  </div>`;
255
- return;
256
- s(`.btn-uninstall-service-controller`).classList.add('hide');
257
- EventsUI.onClick(`.btn-install-service-controller`, async (e) => {
258
- e.preventDefault();
259
- const result = await this.install();
260
- s(`.btn-install-service-controller`).classList.add('hide');
261
- s(`.btn-uninstall-service-controller`).classList.remove('hide');
262
- });
263
- EventsUI.onClick(`.btn-uninstall-service-controller`, async (e) => {
264
- e.preventDefault();
265
- const result = await this.uninstall();
266
- s(`.btn-uninstall-service-controller`).classList.add('hide');
267
- s(`.btn-install-service-controller`).classList.remove('hide');
268
- });
269
- EventsUI.onClick(`.btn-reload`, async (e) => {
270
- e.preventDefault();
271
- location.reload();
272
- });
273
- const workerStatus = await this.status();
274
- if (workerStatus) {
275
- s(`.btn-install-service-controller`).classList.add('hide');
276
- s(`.btn-uninstall-service-controller`).classList.remove('hide');
277
- } else {
278
- s(`.btn-uninstall-service-controller`).classList.add('hide');
279
- s(`.btn-install-service-controller`).classList.remove('hide');
280
- }
281
- },
282
- };
360
+ }
361
+ }
362
+
363
+ // Create the singleton instance
364
+ const PwaWorkerInstance = new PwaWorker();
365
+
366
+ // Export the new class name for modern usage
367
+ export { PwaWorker };
283
368
 
284
- export { Worker };
369
+ // Export the instance with the old name (`Worker`) for backward compatibility,
370
+ // ensuring existing code consuming the module continues to work.
371
+ export { PwaWorkerInstance as Worker };
@@ -186,6 +186,7 @@ const MenuDefault = {
186
186
  })}
187
187
  ${await BtnIcon.Render({
188
188
  class: 'in wfa main-btn-menu main-btn-chat',
189
+ useMenuBtn: true,
189
190
  label: html`${renderMenuLabel({
190
191
  icon: html`<i class="far fa-comments"></i>`,
191
192
  text: html`<span class="menu-label-text">${Translate.Render('chat')}</span>`,
package/src/client.dev.js CHANGED
@@ -6,16 +6,19 @@
6
6
  import dotenv from 'dotenv';
7
7
  import { loggerFactory } from './server/logger.js';
8
8
  import { ProcessController } from './server/process.js';
9
- import { Config } from './server/conf.js';
9
+ import { Config, buildClientStaticConf } from './server/conf.js';
10
10
  import { createClientDevServer } from './server/client-dev-server.js';
11
- dotenv.config();
12
11
 
13
- await Config.build();
12
+ dotenv.config();
14
13
 
15
14
  const logger = loggerFactory(import.meta);
16
15
 
17
16
  await logger.setUpInfo();
18
17
 
18
+ await buildClientStaticConf();
19
+
20
+ await Config.build();
21
+
19
22
  await createClientDevServer();
20
23
 
21
24
  ProcessController.init(logger);
@@ -1,29 +1,73 @@
1
1
  import { MongooseDB } from './mongo/MongooseDB.js';
2
2
  import { loggerFactory } from '../server/logger.js';
3
3
 
4
+ /**
5
+ * Module for managing and loading various database connections (e.g., Mongoose, MariaDB).
6
+ * @module src/db/DataBaseProvider.js
7
+ * @namespace DataBaseProviderNamespace
8
+ */
9
+
4
10
  const logger = loggerFactory(import.meta);
5
11
 
6
- const DataBaseProvider = {
7
- instance: {},
8
- load: async function (options = { apis: [], host: '', path: '', db: {} }) {
12
+ /**
13
+ * @class
14
+ * @alias DataBaseProviderService
15
+ * @memberof DataBaseProviderNamespace
16
+ * @classdesc Centralized service for loading, managing, and accessing multiple database connections
17
+ * based on application configuration (host, path, provider type).
18
+ */
19
+ class DataBaseProviderService {
20
+ /**
21
+ * Internal storage for database connection instances, keyed by host+path.
22
+ * @type {object.<string, object>}
23
+ * @private
24
+ */
25
+ #instance = {};
26
+
27
+ /**
28
+ * Retrieves the internal instance storage for direct access (used for backward compatibility).
29
+ * @returns {object.<string, object>} The internal connection instance map.
30
+ */
31
+ get instance() {
32
+ return this.#instance;
33
+ }
34
+
35
+ /**
36
+ * Loads and initializes a database provider based on the configuration.
37
+ * If the connection is already loaded for the given host/path, it returns the existing instance.
38
+ *
39
+ * @async
40
+ * @param {object} [options] - Configuration for the database connection.
41
+ * @param {Array<string>} [options.apis=[]] - List of APIs whose models should be loaded (for Mongoose).
42
+ * @param {string} [options.host=''] - The host part of the application context (e.g., domain).
43
+ * @param {string} [options.path=''] - The path part of the application context.
44
+ * @param {object} [options.db={}] - The specific database configuration object.
45
+ * @param {string} options.db.provider - The name of the database provider ('mongoose', 'mariadb', etc.).
46
+ * @param {string} options.db.host - The database server host.
47
+ * @param {string} options.db.name - The database name.
48
+ * @returns {Promise<object|undefined>} A promise that resolves to the initialized provider object
49
+ * or `undefined` on error or if the provider is already loaded.
50
+ */
51
+ async load(options = { apis: [], host: '', path: '', db: {} }) {
9
52
  try {
10
53
  const { apis, host, path, db } = options;
54
+ const key = `${host}${path}`;
11
55
 
12
- if (!this.instance[`${host}${path}`]) this.instance[`${host}${path}`] = {};
56
+ if (!this.#instance[key]) this.#instance[key] = {};
13
57
 
14
- if (!db || this.instance[`${host}${path}`][db.provider]) return;
58
+ if (!db || this.#instance[key][db.provider]) return this.#instance[key][db.provider];
15
59
 
16
- // logger.info(`Load ${db.provider} provider`, `${host}${path}`);
60
+ // logger.info(`Load ${db.provider} provider`, key);
17
61
  switch (db.provider) {
18
62
  case 'mongoose':
19
63
  {
20
64
  const conn = await MongooseDB.connect(db.host, db.name);
21
- this.instance[`${host}${path}`][db.provider] = {
65
+ this.#instance[key][db.provider] = {
22
66
  models: await MongooseDB.loadModels({ conn, apis }),
23
67
  connection: conn,
24
68
  close: async () => {
25
69
  return await new Promise((resolve) => {
26
- DataBaseProvider.instance[`${host}${path}`][db.provider].connection.close().then(() => {
70
+ this.#instance[key][db.provider].connection.close().then(() => {
27
71
  // logger.info('Mongoose connection is disconnected', db);
28
72
  return resolve();
29
73
  });
@@ -35,11 +79,20 @@ const DataBaseProvider = {
35
79
  default:
36
80
  break;
37
81
  }
38
- return this.instance[`${host}${path}`][db.provider];
82
+ return this.#instance[key][db.provider];
39
83
  } catch (error) {
40
84
  logger.error(error, { error: error.stack, options });
41
85
  return undefined;
42
86
  }
43
- },
44
- };
45
- export { DataBaseProvider };
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Singleton instance of the DataBaseProviderService class for backward compatibility.
92
+ * @alias DataBaseProvider
93
+ * @memberof DataBaseProviderNamespace
94
+ * @type {DataBaseProviderService}
95
+ */
96
+ const DataBaseProvider = new DataBaseProviderService();
97
+
98
+ export { DataBaseProvider, DataBaseProviderService as DataBaseProviderClass };
@@ -2,10 +2,35 @@ import mariadb from 'mariadb';
2
2
 
3
3
  import { loggerFactory } from '../../server/logger.js';
4
4
 
5
+ /**
6
+ * Module for interacting with MariaDB/MySQL databases using the mariadb connector.
7
+ * @module src/db/MariaDB.js
8
+ * @namespace MariaDBNamespace
9
+ */
10
+
5
11
  const logger = loggerFactory(import.meta);
6
12
 
7
- const MariaDB = {
8
- query: async (options) => {
13
+ /**
14
+ * @class
15
+ * @alias MariaDBService
16
+ * @memberof MariaDBNamespace
17
+ * @classdesc Provides a simplified interface for executing queries against a MariaDB/MySQL database
18
+ * using a connection pool, ensuring connection management (acquisition and release).
19
+ */
20
+ class MariaDBService {
21
+ /**
22
+ * Executes a SQL query against the MariaDB database.
23
+ *
24
+ * @async
25
+ * @param {object} options - The database connection and query options.
26
+ * @param {string} [options.host='127.0.0.1'] - The database host.
27
+ * @param {number} [options.port=3306] - The database port.
28
+ * @param {string} [options.user='root'] - The database user.
29
+ * @param {string} [options.password=''] - The database password.
30
+ * @param {string} options.query - The SQL query string to execute.
31
+ * @returns {Promise<any>} The result of the database query.
32
+ */
33
+ async query(options) {
9
34
  const { host, port, user, password, query } = options;
10
35
  const pool = mariadb.createPool({
11
36
  host: 'host' in options ? host : '127.0.0.1',
@@ -22,12 +47,20 @@ const MariaDB = {
22
47
  if (error.stack.startsWith('TypeError: Do not know how to serialize a BigInt')) return;
23
48
  logger.error(error, error.stack);
24
49
  } finally {
25
- if (conn) conn.release(); //release to pool
50
+ if (conn) conn.release(); // release to pool
26
51
  await pool.end();
27
52
  }
28
53
 
29
54
  return result;
30
- },
31
- };
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Singleton instance of the MariaDBService class for backward compatibility.
60
+ * @alias MariaDB
61
+ * @memberof MariaDBNamespace
62
+ * @type {MariaDBService}
63
+ */
64
+ const MariaDB = new MariaDBService();
32
65
 
33
- export { MariaDB };
66
+ export { MariaDB, MariaDBService as MariaDBClass };