@sqb/nestjs 4.21.0 → 4.22.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/README.md CHANGED
@@ -41,6 +41,38 @@ $ npm install @sqb/nestjs --save
41
41
 
42
42
  - node >= 16.x
43
43
 
44
+ ## Environment Variables
45
+
46
+ The library supports configuration through environment variables. Environment variables below is accepted.
47
+ All environment variables starts with prefix (SQB\_). This can be configured while registering the module.
48
+
49
+ <!--- BEGIN env --->
50
+
51
+ | Environment Variable | Type | Default | Description |
52
+ |------------------------------|---------|---------|-------------------------------------------------------|
53
+ | SQB_DIALECT | String | | Database dialect (e.g. postgres, mysql, oracle) |
54
+ | SQB_CONNECTION_NAME | String | | Logical name of the database connection |
55
+ | SQB_HOST | String | | Database server host address |
56
+ | SQB_PORT | Number | | Database server port |
57
+ | SQB_DATABASE | String | | Database name |
58
+ | SQB_SCHEMA | String | | Default database schema |
59
+ | SQB_USER | String | | Database user name |
60
+ | SQB_PASSWORD | String | | Database user password |
61
+ | SQB_DRIVER | String | | Database driver identifier |
62
+ | SQB_POOL_MAX | Number | | Maximum number of connections in the pool |
63
+ | SQB_POOL_MIN | Number | | Minimum number of connections in the pool |
64
+ | SQB_POOL_IDLE_TIMEOUT | Number | | Time (ms) before an idle connection is released |
65
+ | SQB_POOL_ACQUIRE_TIMEOUT | Number | | Timeout (ms) for acquiring a connection |
66
+ | SQB_POOL_ACQUIRE_MAX_RETRIES | Number | | Maximum number of retries when acquiring a connection |
67
+ | SQB_POOL_ACQUIRE_RETRY_WAIT | Number | | Wait time (ms) between acquire retry attempts |
68
+ | SQB_POOL_FIFO | Boolean | | Whether the pool queue operates in FIFO mode |
69
+ | SQB_POOL_MAX_QUEUE | Number | | Maximum number of queued connection requests |
70
+ | SQB_POOL_MIN_IDLE | Number | | Minimum number of idle connections to keep |
71
+ | SQB_POOL_VALIDATION | Boolean | | Enables validation of connections before use |
72
+ | SQB_POOL_HOUSE_KEEP_INTERVAL | Number | | Interval (ms) for pool housekeeping tasks |
73
+
74
+ <!--- END env --->
75
+
44
76
  ### License
45
77
 
46
78
  SQB is available under [MIT](LICENSE) license.
@@ -61,3 +93,4 @@ SQB is available under [MIT](LICENSE) license.
61
93
  [devdependencies-url]: https://david-dm.org/sqbjs/@sqb/nestjs?type=dev
62
94
  [quality-image]: http://npm.packagequality.com/shield/@sqb/nestjs.png
63
95
  [quality-url]: http://packagequality.com/#?package=@sqb/nestjs
96
+
@@ -1,2 +1,2 @@
1
- import { SqbClientConnectionOptions } from './sqb.interface.js';
1
+ import type { SqbClientConnectionOptions } from './sqb.interface.js';
2
2
  export declare function getSqbConfig(moduleOptions: SqbClientConnectionOptions, prefix?: string): SqbClientConnectionOptions;
package/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- export * from './sqb.decorators.js';
2
1
  export * from './sqb.interface.js';
3
2
  export * from './sqb.module.js';
4
- export * from './sqb.utils.js';
5
3
  export { SqbClient } from '@sqb/connect';
package/index.js CHANGED
@@ -1,5 +1,3 @@
1
- export * from './sqb.decorators.js';
2
1
  export * from './sqb.interface.js';
3
2
  export * from './sqb.module.js';
4
- export * from './sqb.utils.js';
5
3
  export { SqbClient } from '@sqb/connect';
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@sqb/nestjs",
3
3
  "description": "Nestjs module for data connection using SQB",
4
- "version": "4.21.0",
4
+ "version": "4.22.0",
5
5
  "author": "Panates",
6
6
  "license": "Apache-2.0",
7
7
  "dependencies": {
8
- "@jsopen/objects": "^2.0.2",
9
- "putil-varhelpers": "^1.6.5",
8
+ "@jsopen/objects": "^2.1.1",
9
+ "ansi-colors": "^4.1.3",
10
+ "putil-varhelpers": "^1.7.0",
10
11
  "tslib": "^2.8.1"
11
12
  },
12
13
  "peerDependencies": {
13
14
  "@nestjs/common": ">=7.4.0",
14
15
  "@nestjs/core": ">=7.4.0",
15
- "@sqb/builder": "^4.21.0",
16
- "@sqb/connect": "^4.21.0",
16
+ "@sqb/builder": "^4.22.0",
17
+ "@sqb/connect": "^4.22.0",
17
18
  "reflect-metadata": "^0.2.2",
18
19
  "rxjs": ">=6.6.0"
19
20
  },
@@ -1,14 +1,14 @@
1
- import { DynamicModule, OnApplicationShutdown } from '@nestjs/common';
2
- import { ModuleRef } from '@nestjs/core';
3
- import type { SqbModuleAsyncOptions, SqbModuleOptions } from './sqb.interface.js';
4
- export declare class SqbCoreModule implements OnApplicationShutdown {
5
- private readonly options;
6
- private readonly moduleRef;
7
- constructor(options: SqbModuleOptions, moduleRef: ModuleRef);
8
- static forRoot(options?: SqbModuleOptions): DynamicModule;
9
- static forRootAsync(options: SqbModuleAsyncOptions): DynamicModule;
1
+ import { DynamicModule, Logger, OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
2
+ import { SqbClient } from '@sqb/connect';
3
+ import type { SqbClientConnectionOptions, SqbModuleAsyncOptions, SqbModuleOptions } from './sqb.interface.js';
4
+ export declare class SqbCoreModule implements OnApplicationBootstrap, OnApplicationShutdown {
5
+ protected readonly client: SqbClient;
6
+ private readonly connectionOptions;
7
+ private logger;
8
+ static forRoot(moduleOptions?: SqbModuleOptions): DynamicModule;
9
+ static forRootAsync(asyncOptions: SqbModuleAsyncOptions): DynamicModule;
10
+ private static _createDynamicModule;
11
+ constructor(client: SqbClient, connectionOptions: SqbClientConnectionOptions, logger: Logger);
12
+ onApplicationBootstrap(): Promise<void> | undefined;
10
13
  onApplicationShutdown(): Promise<void>;
11
- private static createAsyncProviders;
12
- private static createAsyncOptionsProvider;
13
- private static createConnection;
14
14
  }
@@ -1,126 +1,112 @@
1
1
  var SqbCoreModule_1;
2
2
  import { __decorate, __metadata, __param } from "tslib";
3
- import { Global, Inject, Module, } from '@nestjs/common';
4
- import { ModuleRef } from '@nestjs/core';
3
+ import assert from 'node:assert';
4
+ import { Global, Inject, Logger, Module, } from '@nestjs/common';
5
5
  import { SqbClient } from '@sqb/connect';
6
+ import colors from 'ansi-colors';
6
7
  import * as crypto from 'crypto';
7
- import { defer } from 'rxjs';
8
- import * as rxjs from 'rxjs';
9
8
  import { getSqbConfig } from './get-sqb-config.js';
10
9
  import { SQB_CONNECTION_OPTIONS, SQB_MODULE_ID, SQB_MODULE_OPTIONS, } from './sqb.constants.js';
11
- import { getSQBToken, handleRetry } from './sqb.utils.js';
10
+ const CLIENT_TOKEN = Symbol('CLIENT_TOKEN');
12
11
  let SqbCoreModule = SqbCoreModule_1 = class SqbCoreModule {
13
- options;
14
- moduleRef;
15
- constructor(options, moduleRef) {
16
- this.options = options;
17
- this.moduleRef = moduleRef;
18
- }
19
- static forRoot(options = {}) {
20
- const optionsProvider = {
21
- provide: SQB_MODULE_OPTIONS,
22
- useValue: options,
23
- };
24
- const connectionProvider = {
25
- provide: getSQBToken(options.name),
26
- inject: [SQB_CONNECTION_OPTIONS],
27
- useFactory: (sqbConnectionOptions) => this.createConnection(options, sqbConnectionOptions),
28
- };
29
- return {
30
- module: SqbCoreModule_1,
12
+ client;
13
+ connectionOptions;
14
+ logger;
15
+ static forRoot(moduleOptions = {}) {
16
+ const connectionOptions = getSqbConfig(moduleOptions.useValue || {}, moduleOptions.envPrefix);
17
+ return this._createDynamicModule(moduleOptions, {
18
+ global: moduleOptions.global,
31
19
  providers: [
32
- connectionProvider,
33
- optionsProvider,
34
20
  {
35
21
  provide: SQB_CONNECTION_OPTIONS,
36
- useValue: getSqbConfig(options.useValue || {}, options.envPrefix),
22
+ useValue: connectionOptions,
37
23
  },
38
24
  ],
39
- exports: [connectionProvider],
40
- };
25
+ });
41
26
  }
42
- static forRootAsync(options) {
43
- const connectionProvider = {
44
- provide: getSQBToken(options.name),
45
- inject: [SQB_MODULE_OPTIONS, SQB_CONNECTION_OPTIONS],
46
- useFactory: async (sqbOptions, sqbConnectionOptions) => this.createConnection(sqbOptions, sqbConnectionOptions),
47
- };
48
- const asyncProviders = this.createAsyncProviders(options);
49
- return {
50
- module: SqbCoreModule_1,
51
- imports: options.imports,
27
+ static forRootAsync(asyncOptions) {
28
+ assert.ok(asyncOptions.useFactory, 'useFactory is required');
29
+ return this._createDynamicModule(asyncOptions, {
30
+ global: asyncOptions.global,
52
31
  providers: [
53
- ...asyncProviders,
54
- connectionProvider,
55
- {
56
- provide: SQB_MODULE_OPTIONS,
57
- useValue: options,
58
- },
59
32
  {
60
- provide: SQB_MODULE_ID,
61
- useValue: crypto.randomUUID(),
33
+ provide: SQB_CONNECTION_OPTIONS,
34
+ inject: asyncOptions.inject,
35
+ useFactory: async (...args) => {
36
+ const opts = await asyncOptions.useFactory(...args);
37
+ return getSqbConfig(opts, asyncOptions.envPrefix);
38
+ },
62
39
  },
63
40
  ],
64
- exports: [connectionProvider],
65
- };
41
+ });
66
42
  }
67
- async onApplicationShutdown() {
68
- const client = this.moduleRef.get(getSQBToken(this.options.name));
69
- const op = this.moduleRef.get(SQB_CONNECTION_OPTIONS);
70
- if (client)
71
- await client.close(op.shutdownWaitMs);
43
+ static _createDynamicModule(opts, metadata) {
44
+ const token = opts.token || SqbClient;
45
+ const providers = [
46
+ ...(metadata.providers ?? []),
47
+ {
48
+ provide: SQB_MODULE_OPTIONS,
49
+ useValue: opts,
50
+ },
51
+ {
52
+ provide: token,
53
+ inject: [SQB_CONNECTION_OPTIONS],
54
+ useFactory: (sqbConnectionOptions) => new SqbClient(sqbConnectionOptions),
55
+ },
56
+ {
57
+ provide: CLIENT_TOKEN,
58
+ useExisting: token,
59
+ },
60
+ {
61
+ provide: SQB_MODULE_ID,
62
+ useValue: crypto.randomUUID(),
63
+ },
64
+ {
65
+ provide: Logger,
66
+ useValue: opts.logger || new Logger('SQB'),
67
+ },
68
+ ];
69
+ return {
70
+ module: SqbCoreModule_1,
71
+ ...metadata,
72
+ providers,
73
+ exports: [...(metadata.exports ?? []), SQB_CONNECTION_OPTIONS, token],
74
+ };
72
75
  }
73
- static createAsyncProviders(options) {
74
- if (options.useExisting || options.useFactory)
75
- return [this.createAsyncOptionsProvider(options)];
76
- if (options.useClass) {
77
- return [
78
- this.createAsyncOptionsProvider(options),
79
- {
80
- provide: options.useClass,
81
- useClass: options.useClass,
82
- },
83
- ];
84
- }
85
- throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
76
+ constructor(client, connectionOptions, logger) {
77
+ this.client = client;
78
+ this.connectionOptions = connectionOptions;
79
+ this.logger = logger;
86
80
  }
87
- static createAsyncOptionsProvider(options) {
88
- if (options.useFactory) {
89
- return {
90
- provide: SQB_CONNECTION_OPTIONS,
91
- useFactory: options.useFactory,
92
- inject: options.inject || [],
93
- };
94
- }
95
- const useClass = options.useClass || options.useExisting;
96
- if (useClass) {
97
- return {
98
- provide: SQB_CONNECTION_OPTIONS,
99
- useFactory: (optionsFactory) => optionsFactory.createSqbOptions(options.name),
100
- inject: [useClass],
101
- };
102
- }
103
- throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
81
+ onApplicationBootstrap() {
82
+ if (this.connectionOptions.lazyConnect)
83
+ return;
84
+ Logger.flush();
85
+ const logTimer = setTimeout(() => {
86
+ this.logger?.verbose(`Waiting to connect to Database [${colors.blue(this.connectionOptions.dialect || '')}]`);
87
+ }, 1000);
88
+ return this.client
89
+ .test()
90
+ .catch(e => {
91
+ clearTimeout(logTimer);
92
+ this.logger?.error('Database connection failed: ' + e.message);
93
+ throw e;
94
+ })
95
+ .then(() => {
96
+ clearTimeout(logTimer);
97
+ this.logger?.log(`Database connection established`);
98
+ });
104
99
  }
105
- static async createConnection(moduleOptions, sqbConnectionOptions) {
106
- const connectionToken = moduleOptions.name;
107
- // NestJS 8
108
- // @ts-ignore
109
- if (rxjs.lastValueFrom) {
110
- // @ts-ignore
111
- return await rxjs.lastValueFrom(defer(async () => new SqbClient(sqbConnectionOptions)).pipe(handleRetry(connectionToken, sqbConnectionOptions.retryAttempts, sqbConnectionOptions.retryDelay, sqbConnectionOptions.verboseRetryLog, sqbConnectionOptions.toRetry)));
112
- }
113
- // NestJS 7
114
- // @ts-ignore
115
- return await defer(async () => new SqbClient(sqbConnectionOptions))
116
- .pipe(handleRetry(connectionToken, sqbConnectionOptions.retryAttempts, sqbConnectionOptions.retryDelay, sqbConnectionOptions.verboseRetryLog, sqbConnectionOptions.toRetry))
117
- .toPromise();
100
+ async onApplicationShutdown() {
101
+ await this.client.close(this.connectionOptions.shutdownWaitMs);
118
102
  }
119
103
  };
120
104
  SqbCoreModule = SqbCoreModule_1 = __decorate([
121
105
  Global(),
122
106
  Module({}),
123
- __param(0, Inject(SQB_MODULE_OPTIONS)),
124
- __metadata("design:paramtypes", [Object, ModuleRef])
107
+ __param(0, Inject(CLIENT_TOKEN)),
108
+ __param(1, Inject(SQB_CONNECTION_OPTIONS)),
109
+ __param(2, Inject(Logger)),
110
+ __metadata("design:paramtypes", [SqbClient, Object, Logger])
125
111
  ], SqbCoreModule);
126
112
  export { SqbCoreModule };
@@ -1,56 +1,29 @@
1
- import { LoggerService, Type } from '@nestjs/common';
2
- import { ModuleMetadata } from '@nestjs/common/interfaces';
3
- import { ClientConfiguration } from '@sqb/connect';
1
+ import type { InjectionToken, LoggerService } from '@nestjs/common';
2
+ import type { ModuleMetadata } from '@nestjs/common/interfaces';
3
+ import type { ClientConfiguration } from '@sqb/connect';
4
4
  export interface SqbClientConnectionOptions extends ClientConfiguration {
5
- /**
6
- * Number of times to retry connecting
7
- * Default: 10
8
- */
9
- retryAttempts?: number;
10
- /**
11
- * Delay between connection retry attempts (ms)
12
- * Default: 3000
13
- */
14
- retryDelay?: number;
15
- /**
16
- * Function that determines whether the module should
17
- * attempt to connect upon failure.
18
- *
19
- * @param err error that was thrown
20
- * @returns whether to retry connection or not
21
- */
22
- toRetry?: (err: any) => boolean;
23
- /**
24
- * If `true`, connection will not be closed on application shutdown.
25
- */
26
- keepConnectionAlive?: boolean;
27
- /**
28
- * If `true`, will show verbose error messages on each connection retry.
29
- */
30
- verboseRetryLog?: boolean;
31
5
  /**
32
6
  * Number of ms to wait closing connection on shutdown
33
7
  * Default: 10
34
8
  */
35
9
  shutdownWaitMs?: number;
10
+ /**
11
+ * If `true`, will not connect to database on application start
12
+ * Default: `false`
13
+ */
14
+ lazyConnect?: boolean;
36
15
  }
37
16
  interface BaseModuleOptions {
17
+ token?: InjectionToken;
38
18
  envPrefix?: string;
39
19
  logger?: LoggerService | string;
40
20
  global?: boolean;
41
- name?: string;
42
21
  }
43
22
  export interface SqbModuleOptions extends BaseModuleOptions {
44
23
  useValue?: SqbClientConnectionOptions;
45
24
  }
46
- export interface SqbOptionsFactory {
47
- createSqbOptions(connectionName?: string): Promise<SqbClientConnectionOptions> | SqbClientConnectionOptions;
48
- }
49
- export interface SqbModuleAsyncOptions extends BaseModuleOptions, Pick<ModuleMetadata, 'imports'> {
50
- name?: string;
51
- useExisting?: Type<SqbOptionsFactory>;
52
- useClass?: Type<SqbOptionsFactory>;
53
- useFactory?: (...args: any[]) => Promise<SqbClientConnectionOptions> | SqbClientConnectionOptions;
25
+ export interface SqbModuleAsyncOptions extends BaseModuleOptions, Partial<Pick<ModuleMetadata, 'imports'>> {
54
26
  inject?: any[];
27
+ useFactory?: (...args: any[]) => Promise<SqbClientConnectionOptions> | SqbClientConnectionOptions;
55
28
  }
56
29
  export {};
@@ -1 +0,0 @@
1
- export declare const InjectSQB: (name?: string) => ParameterDecorator;
package/sqb.decorators.js DELETED
@@ -1,3 +0,0 @@
1
- import { Inject } from '@nestjs/common';
2
- import { getSQBToken } from './sqb.utils.js';
3
- export const InjectSQB = (name) => Inject(getSQBToken(name));
package/sqb.utils.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import { Type } from '@nestjs/common';
2
- import { SqbClient } from '@sqb/connect';
3
- import { Observable } from 'rxjs';
4
- /**
5
- * This function returns a Connection injection token for the given connection name.
6
- * @param {string | symbol} [name=SQB_DEFAULT_CONNECTION] This optional parameter is either
7
- * a SqbClient, or a ConnectionOptions or a string.
8
- * @returns {string | symbol} The Connection injection token.
9
- */
10
- export declare function getSQBToken(name?: string | symbol | Type<SqbClient>): string | symbol | Type<SqbClient>;
11
- export declare function handleRetry(connectionName?: string | symbol | Type<SqbClient>, retryAttempts?: number, retryDelay?: number, verboseRetryLog?: boolean, toRetry?: (err: any) => boolean): <T>(source: Observable<T>) => Observable<T>;
package/sqb.utils.js DELETED
@@ -1,35 +0,0 @@
1
- import { Logger } from '@nestjs/common';
2
- import { SqbClient } from '@sqb/connect';
3
- import { delay, retryWhen, scan } from 'rxjs/operators';
4
- const logger = new Logger('SqbModule');
5
- /**
6
- * This function returns a Connection injection token for the given connection name.
7
- * @param {string | symbol} [name=SQB_DEFAULT_CONNECTION] This optional parameter is either
8
- * a SqbClient, or a ConnectionOptions or a string.
9
- * @returns {string | symbol} The Connection injection token.
10
- */
11
- export function getSQBToken(name) {
12
- if (!name)
13
- return SqbClient;
14
- if (typeof name === 'symbol' || typeof name === 'function')
15
- return name;
16
- return `${name}_SqbConnection`;
17
- }
18
- export function handleRetry(connectionName, retryAttempts = 9, retryDelay = 3000, verboseRetryLog = false, toRetry) {
19
- return (source) => source.pipe(retryWhen(e => e.pipe(scan((errorCount, error) => {
20
- if (toRetry && !toRetry(error)) {
21
- throw error;
22
- }
23
- const connectionInfo = !connectionName || connectionName === SqbClient
24
- ? 'default'
25
- : ` (${String(connectionName)})`;
26
- const verboseMessage = verboseRetryLog
27
- ? ` Message: ${error.message}.`
28
- : '';
29
- logger.error(`Unable to connect to the database ${connectionInfo}.${verboseMessage} Retrying (${errorCount + 1})...`, error.stack);
30
- if (errorCount + 1 >= retryAttempts) {
31
- throw error;
32
- }
33
- return errorCount + 1;
34
- }, 0), delay(retryDelay))));
35
- }