@sqb/nestjs 4.21.1 → 4.23.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.
@@ -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.1",
4
+ "version": "4.23.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.2.0",
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.1",
16
- "@sqb/connect": "^4.21.1",
16
+ "@sqb/builder": "^4.23.0",
17
+ "@sqb/connect": "^4.23.0",
17
18
  "reflect-metadata": "^0.2.2",
18
19
  "rxjs": ">=6.6.0"
19
20
  },
@@ -1,16 +1,14 @@
1
- import { DynamicModule, Logger, 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
- private logger?;
8
- constructor(options: SqbModuleOptions, moduleRef: ModuleRef, logger?: Logger | undefined);
9
- static forRoot(options?: SqbModuleOptions): DynamicModule;
10
- 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);
11
12
  onApplicationBootstrap(): Promise<void> | undefined;
12
13
  onApplicationShutdown(): Promise<void>;
13
- private static createAsyncProviders;
14
- private static createAsyncOptionsProvider;
15
- private static createConnection;
16
14
  }
@@ -1,153 +1,112 @@
1
1
  var SqbCoreModule_1;
2
2
  import { __decorate, __metadata, __param } from "tslib";
3
+ import assert from 'node:assert';
3
4
  import { Global, Inject, Logger, Module, } from '@nestjs/common';
4
- import { ModuleRef } from '@nestjs/core';
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;
12
+ client;
13
+ connectionOptions;
15
14
  logger;
16
- constructor(options, moduleRef, logger) {
17
- this.options = options;
18
- this.moduleRef = moduleRef;
19
- this.logger = logger;
20
- }
21
- static forRoot(options = {}) {
22
- const optionsProvider = {
23
- provide: SQB_MODULE_OPTIONS,
24
- useValue: options,
25
- };
26
- const connectionProvider = {
27
- provide: getSQBToken(options.name),
28
- inject: [SQB_CONNECTION_OPTIONS],
29
- useFactory: (sqbConnectionOptions) => this.createConnection(options, sqbConnectionOptions),
30
- };
31
- return {
32
- module: SqbCoreModule_1,
15
+ static forRoot(moduleOptions = {}) {
16
+ const connectionOptions = getSqbConfig(moduleOptions.useValue || {}, moduleOptions.envPrefix);
17
+ return this._createDynamicModule(moduleOptions, {
18
+ global: moduleOptions.global,
33
19
  providers: [
34
- connectionProvider,
35
- optionsProvider,
36
20
  {
37
21
  provide: SQB_CONNECTION_OPTIONS,
38
- useValue: getSqbConfig(options.useValue || {}, options.envPrefix),
39
- },
40
- {
41
- provide: Logger,
42
- useValue: typeof options.logger === 'string'
43
- ? new Logger(options.logger)
44
- : options.logger,
22
+ useValue: connectionOptions,
45
23
  },
46
24
  ],
47
- exports: [connectionProvider],
48
- };
25
+ });
49
26
  }
50
- static forRootAsync(options) {
51
- const connectionProvider = {
52
- provide: getSQBToken(options.name),
53
- inject: [SQB_MODULE_OPTIONS, SQB_CONNECTION_OPTIONS],
54
- useFactory: async (sqbOptions, sqbConnectionOptions) => this.createConnection(sqbOptions, sqbConnectionOptions),
55
- };
56
- const asyncProviders = this.createAsyncProviders(options);
57
- return {
58
- module: SqbCoreModule_1,
59
- imports: options.imports,
27
+ static forRootAsync(asyncOptions) {
28
+ assert.ok(asyncOptions.useFactory, 'useFactory is required');
29
+ return this._createDynamicModule(asyncOptions, {
30
+ global: asyncOptions.global,
60
31
  providers: [
61
- ...asyncProviders,
62
- connectionProvider,
63
- {
64
- provide: SQB_MODULE_OPTIONS,
65
- useValue: options,
66
- },
67
32
  {
68
- provide: SQB_MODULE_ID,
69
- useValue: crypto.randomUUID(),
70
- },
71
- {
72
- provide: Logger,
73
- useValue: typeof options.logger === 'string'
74
- ? new Logger(options.logger)
75
- : options.logger,
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
+ },
76
39
  },
77
40
  ],
78
- exports: [connectionProvider],
41
+ });
42
+ }
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],
79
74
  };
80
75
  }
76
+ constructor(client, connectionOptions, logger) {
77
+ this.client = client;
78
+ this.connectionOptions = connectionOptions;
79
+ this.logger = logger;
80
+ }
81
81
  onApplicationBootstrap() {
82
- const client = this.moduleRef.get(getSQBToken(this.options.name));
83
- const op = this.moduleRef.get(SQB_CONNECTION_OPTIONS);
84
- if (this.options.lazyConnect)
82
+ if (this.connectionOptions.lazyConnect)
85
83
  return;
86
- this.logger?.log(`Connecting to database [${op.database}] at ${op.host}:${op.port} using ${op.dialect} dialect`);
87
84
  Logger.flush();
88
- return client.test().catch(e => {
89
- this.logger?.error(`${op.dialect} connection failed: ` + e.message);
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);
90
93
  throw e;
94
+ })
95
+ .then(() => {
96
+ clearTimeout(logTimer);
97
+ this.logger?.log(`Database connection established`);
91
98
  });
92
99
  }
93
100
  async onApplicationShutdown() {
94
- const client = this.moduleRef.get(getSQBToken(this.options.name));
95
- const op = this.moduleRef.get(SQB_CONNECTION_OPTIONS);
96
- if (client)
97
- await client.close(op.shutdownWaitMs);
98
- }
99
- static createAsyncProviders(options) {
100
- if (options.useExisting || options.useFactory)
101
- return [this.createAsyncOptionsProvider(options)];
102
- if (options.useClass) {
103
- return [
104
- this.createAsyncOptionsProvider(options),
105
- {
106
- provide: options.useClass,
107
- useClass: options.useClass,
108
- },
109
- ];
110
- }
111
- throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
112
- }
113
- static createAsyncOptionsProvider(options) {
114
- if (options.useFactory) {
115
- return {
116
- provide: SQB_CONNECTION_OPTIONS,
117
- useFactory: options.useFactory,
118
- inject: options.inject || [],
119
- };
120
- }
121
- const useClass = options.useClass || options.useExisting;
122
- if (useClass) {
123
- return {
124
- provide: SQB_CONNECTION_OPTIONS,
125
- useFactory: (optionsFactory) => optionsFactory.createSqbOptions(options.name),
126
- inject: [useClass],
127
- };
128
- }
129
- throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
130
- }
131
- static async createConnection(moduleOptions, sqbConnectionOptions) {
132
- const connectionToken = moduleOptions.name;
133
- // NestJS 8
134
- // @ts-ignore
135
- if (rxjs.lastValueFrom) {
136
- // @ts-ignore
137
- return await rxjs.lastValueFrom(defer(async () => new SqbClient(sqbConnectionOptions)).pipe(handleRetry(connectionToken, sqbConnectionOptions.retryAttempts, sqbConnectionOptions.retryDelay, sqbConnectionOptions.verboseRetryLog, sqbConnectionOptions.toRetry)));
138
- }
139
- // NestJS 7
140
- // @ts-ignore
141
- return await defer(async () => new SqbClient(sqbConnectionOptions))
142
- .pipe(handleRetry(connectionToken, sqbConnectionOptions.retryAttempts, sqbConnectionOptions.retryDelay, sqbConnectionOptions.verboseRetryLog, sqbConnectionOptions.toRetry))
143
- .toPromise();
101
+ await this.client.close(this.connectionOptions.shutdownWaitMs);
144
102
  }
145
103
  };
146
104
  SqbCoreModule = SqbCoreModule_1 = __decorate([
147
105
  Global(),
148
106
  Module({}),
149
- __param(0, Inject(SQB_MODULE_OPTIONS)),
150
- __metadata("design:paramtypes", [Object, ModuleRef,
151
- Logger])
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])
152
111
  ], SqbCoreModule);
153
112
  export { SqbCoreModule };
@@ -1,57 +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
- lazyConnect?: boolean;
43
21
  }
44
22
  export interface SqbModuleOptions extends BaseModuleOptions {
45
23
  useValue?: SqbClientConnectionOptions;
46
24
  }
47
- export interface SqbOptionsFactory {
48
- createSqbOptions(connectionName?: string): Promise<SqbClientConnectionOptions> | SqbClientConnectionOptions;
49
- }
50
- export interface SqbModuleAsyncOptions extends BaseModuleOptions, Pick<ModuleMetadata, 'imports'> {
51
- name?: string;
52
- useExisting?: Type<SqbOptionsFactory>;
53
- useClass?: Type<SqbOptionsFactory>;
54
- useFactory?: (...args: any[]) => Promise<SqbClientConnectionOptions> | SqbClientConnectionOptions;
25
+ export interface SqbModuleAsyncOptions extends BaseModuleOptions, Partial<Pick<ModuleMetadata, 'imports'>> {
55
26
  inject?: any[];
27
+ useFactory?: (...args: any[]) => Promise<SqbClientConnectionOptions> | SqbClientConnectionOptions;
56
28
  }
57
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
- }