├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── index.ts ├── package-lock.json ├── package.json ├── src ├── bullmq.constants.ts ├── bullmq.core-module.ts ├── bullmq.decorators.ts ├── bullmq.interfaces.ts ├── bullmq.module.ts ├── bullmq.utils.ts └── index.ts ├── test ├── index.spec.ts ├── jest.config.json ├── module.spec.ts └── module.test.ts ├── tsconfig.build.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | index.js 2 | index.ts 3 | index.d.ts -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: [ 9 | 'plugin:@typescript-eslint/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'prettier', 12 | 'prettier/@typescript-eslint', 13 | ], 14 | root: true, 15 | env: { 16 | node: true, 17 | jest: true, 18 | }, 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/no-explicit-any': 'off', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sviatoslav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NestJS BullMQ 2 | 3 | NPM Version 4 | Package License 5 | 6 | ## Table of Contents 7 | 8 | - [Description](#description) 9 | - [Installation](#installation) 10 | - [Examples](#examples) 11 | - [License](#license) 12 | 13 | ## Description 14 | Integrates BullMQ with Nest 15 | 16 | ## Installation 17 | 18 | ```bash 19 | npm install nestjs-bullmq bullmq 20 | ``` 21 | 22 | You can also use the interactive CLI 23 | 24 | ```sh 25 | npx nestjs-modules 26 | ``` 27 | 28 | ## Examples 29 | 30 | ### BullMQModule.forRoot(options, connection?) 31 | 32 | ```ts 33 | import { Module } from '@nestjs/common'; 34 | import { BullMQModule } from 'nestjs-bullmq'; 35 | import { AppController } from './app.controller'; 36 | 37 | @Module({ 38 | imports: [ 39 | BullMQModule.forRoot({ 40 | name: 'QueueName', 41 | config: { 42 | // url: 'redis://:password@localhost:6379', 43 | connection: { host: 'localhost', port: 6379 }, 44 | }, 45 | }), 46 | ], 47 | controllers: [AppController], 48 | }) 49 | export class AppModule {} 50 | ``` 51 | 52 | ### BullMQModule.forRootAsync(options, connection?) 53 | 54 | ```ts 55 | import { Module } from '@nestjs/common'; 56 | import { BullMQModule } from 'nestjs-bullmq'; 57 | import { AppController } from './app.controller'; 58 | 59 | @Module({ 60 | imports: [ 61 | BullMQModule.forRootAsync({ 62 | name: 'QueueName', 63 | useFactory: () => ({ 64 | config: { 65 | // url: 'redis://:password@localhost:6379', 66 | connection: { host: 'localhost', port: 6379 }, 67 | }, 68 | }), 69 | }), 70 | ], 71 | controllers: [AppController], 72 | }) 73 | export class AppModule {} 74 | ``` 75 | 76 | ### InjectBullMQ(queueName?, connection?) 77 | 78 | ```ts 79 | import { Controller, Get, } from '@nestjs/common'; 80 | import { InjectBullMQ, BullMQ } from 'nestjs-bullmq'; 81 | 82 | @Controller() 83 | export class AppController { 84 | constructor( 85 | @InjectBullMQ('QueueName') private readonly bullMQ: BullMQ, 86 | ) {} 87 | 88 | @Get() 89 | async getHello() { 90 | this.bullMQ.process(async job => { 91 | console.log('process', job); 92 | }) 93 | this.bullMQ.queue.add('myJobName', { foo: 'bar' }); 94 | // this.bullMQ.queueEvents.on(); 95 | // this.bullMQ.queueScheduler.on(); 96 | } 97 | } 98 | ``` 99 | 100 | ## License 101 | 102 | MIT 103 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './dist'; 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | exports.__esModule = true; 6 | __export(require("./dist")); 7 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './dist'; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-bullmq", 3 | "version": "0.1.1", 4 | "description": "Nest - modern, fast, powerful node.js web framework (@bullmq)", 5 | "author": "Sviatoslav H", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/svtslv/nestjs-bullmq" 10 | }, 11 | "keywords": [ 12 | "nest", 13 | "nestjs", 14 | "bull", 15 | "bullmq", 16 | "redis", 17 | "queue" 18 | ], 19 | "files": [ 20 | "dist", 21 | "index.js", 22 | "index.d.ts" 23 | ], 24 | "main": "dist/index.js", 25 | "scripts": { 26 | "fix": "rm -rf node_modules && rm package-lock.json && npm install", 27 | "build": "rm -rf dist && tsc -p tsconfig.build.json", 28 | "format": "prettier --write \"**/*.ts\"", 29 | "lint": "eslint 'lib/**/*.ts' --fix", 30 | "test": "jest --config ./test/jest.config.json --runInBand", 31 | "prepublish:npm": "npm run build", 32 | "publish:npm": "npm publish --access public", 33 | "prepublish:test": "npm run build", 34 | "publish:test": "npm publish --access public --tag test", 35 | "prerelease": "npm run build", 36 | "release": "release-it" 37 | }, 38 | "peerDependencies": { 39 | "@nestjs/common": ">=6.7.0", 40 | "@nestjs/core": ">=6.7.0", 41 | "bullmq": ">=1.8.0" 42 | }, 43 | "devDependencies": { 44 | "@nestjs/common": "^9.2.0", 45 | "@nestjs/core": "^9.0.5", 46 | "@nestjs/platform-express": "^9.2.1", 47 | "@nestjs/testing": "^7.0.3", 48 | "@types/express": "^4.17.6", 49 | "@types/jest": "^26.0.5", 50 | "@types/node": "^13.9.2", 51 | "@types/supertest": "^2.0.8", 52 | "@typescript-eslint/eslint-plugin": "^2.24.0", 53 | "@typescript-eslint/parser": "^2.24.0", 54 | "bullmq": "^1.8.7", 55 | "eslint": "^6.8.0", 56 | "eslint-config-prettier": "^6.10.0", 57 | "eslint-plugin-import": "^2.20.1", 58 | "jest": "^26.1.0", 59 | "prettier": "^1.19.1", 60 | "reflect-metadata": "^0.1.13", 61 | "supertest": "^4.0.2", 62 | "ts-jest": "^26.1.3", 63 | "ts-loader": "^9.4.2", 64 | "ts-node": "^8.7.0", 65 | "tsconfig-paths": "^3.9.0", 66 | "typescript": "^3.8.3" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/bullmq.constants.ts: -------------------------------------------------------------------------------- 1 | export const BULLMQ_MODULE_CONNECTION = 'default'; 2 | export const BULLMQ_MODULE_CONNECTION_TOKEN = 'BullMQModuleConnectionToken'; 3 | export const BULLMQ_MODULE_OPTIONS_TOKEN = 'BullMQModuleOptionsToken'; 4 | -------------------------------------------------------------------------------- /src/bullmq.core-module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module, DynamicModule, Provider } from '@nestjs/common'; 2 | import { BullMQModuleAsyncOptions, BullMQModuleOptions, BullMQModuleOptionsFactory } from './bullmq.interfaces'; 3 | import { createBullMQConnection, getBullMQOptionsToken, getBullMQConnectionToken } from './bullmq.utils' 4 | 5 | @Global() 6 | @Module({}) 7 | export class BullMQCoreModule { 8 | 9 | /* forRoot */ 10 | static forRoot(options: BullMQModuleOptions, connection?: string): DynamicModule { 11 | 12 | const bullMQOptionsProvider: Provider = { 13 | provide: getBullMQOptionsToken(options, connection), 14 | useValue: options, 15 | }; 16 | 17 | const bullMQConnectionProvider: Provider = { 18 | provide: getBullMQConnectionToken(options, connection), 19 | useValue: createBullMQConnection(options, connection), 20 | }; 21 | 22 | return { 23 | module: BullMQCoreModule, 24 | providers: [ 25 | bullMQOptionsProvider, 26 | bullMQConnectionProvider, 27 | ], 28 | exports: [ 29 | bullMQOptionsProvider, 30 | bullMQConnectionProvider, 31 | ], 32 | }; 33 | } 34 | 35 | /* forRootAsync */ 36 | public static forRootAsync(options: BullMQModuleAsyncOptions, connection: string): DynamicModule { 37 | 38 | const bullMQConnectionProvider: Provider = { 39 | provide: getBullMQConnectionToken(options, connection), 40 | useFactory(syncOptions: BullMQModuleOptions) { 41 | syncOptions.name = options.name; 42 | return createBullMQConnection(syncOptions, connection) 43 | }, 44 | inject: [getBullMQOptionsToken(options, connection)], 45 | }; 46 | 47 | return { 48 | module: BullMQCoreModule, 49 | imports: options.imports, 50 | providers: [...this.createAsyncProviders(options, connection), bullMQConnectionProvider], 51 | exports: [bullMQConnectionProvider], 52 | }; 53 | } 54 | 55 | /* createAsyncProviders */ 56 | public static createAsyncProviders(options: BullMQModuleAsyncOptions, connection?: string): Provider[] { 57 | 58 | if(!(options.useExisting || options.useFactory || options.useClass)) { 59 | throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting'); 60 | } 61 | 62 | if (options.useExisting || options.useFactory) { 63 | return [ 64 | this.createAsyncOptionsProvider(options, connection) 65 | ]; 66 | } 67 | 68 | return [ 69 | this.createAsyncOptionsProvider(options, connection), 70 | { provide: options.useClass, useClass: options.useClass }, 71 | ]; 72 | } 73 | 74 | /* createAsyncOptionsProvider */ 75 | public static createAsyncOptionsProvider(options: BullMQModuleAsyncOptions, connection?: string): Provider { 76 | 77 | if(!(options.useExisting || options.useFactory || options.useClass)) { 78 | throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting'); 79 | } 80 | 81 | if (options.useFactory) { 82 | return { 83 | provide: getBullMQOptionsToken(options, connection), 84 | useFactory: options.useFactory, 85 | inject: options.inject || [], 86 | }; 87 | } 88 | 89 | return { 90 | provide: getBullMQOptionsToken(options, connection), 91 | async useFactory(optionsFactory: BullMQModuleOptionsFactory): Promise { 92 | return await optionsFactory.createBullMQModuleOptions(); 93 | }, 94 | inject: [options.useClass || options.useExisting], 95 | }; 96 | } 97 | } -------------------------------------------------------------------------------- /src/bullmq.decorators.ts: -------------------------------------------------------------------------------- 1 | import { Inject } from '@nestjs/common'; 2 | import { getBullMQConnectionToken } from './bullmq.utils'; 3 | 4 | export const InjectBullMQ = (queueName?: string, connection?: string) => { 5 | return Inject(getBullMQConnectionToken({ name: queueName }, connection)); 6 | }; 7 | -------------------------------------------------------------------------------- /src/bullmq.interfaces.ts: -------------------------------------------------------------------------------- 1 | import { ModuleMetadata, Type } from "@nestjs/common/interfaces"; 2 | import type { QueueBaseOptions, Queue, Worker, QueueEvents, QueueScheduler, Job } from 'bullmq'; 3 | 4 | export type BullMQ = { 5 | queue: Queue, 6 | process: (callback: (job: Job) => any) => Worker, 7 | queueEvents: QueueEvents, 8 | queueScheduler: QueueScheduler 9 | } 10 | 11 | export interface BullMQModuleOptions { 12 | name?: string, 13 | config: QueueBaseOptions & { url?: string }; 14 | } 15 | 16 | export interface BullMQModuleOptionsFactory { 17 | createBullMQModuleOptions(): Promise | BullMQModuleOptions; 18 | } 19 | 20 | export interface BullMQModuleAsyncOptions extends Pick { 21 | name?: string, 22 | inject?: any[]; 23 | useClass?: Type; 24 | useExisting?: Type; 25 | useFactory?: (...args: any[]) => Promise | BullMQModuleOptions; 26 | } 27 | -------------------------------------------------------------------------------- /src/bullmq.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module } from '@nestjs/common'; 2 | import { BullMQCoreModule } from './bullmq.core-module'; 3 | import { BullMQModuleAsyncOptions, BullMQModuleOptions } from './bullmq.interfaces'; 4 | 5 | @Module({}) 6 | export class BullMQModule { 7 | public static forRoot(options: BullMQModuleOptions, connection?: string): DynamicModule { 8 | return { 9 | module: BullMQModule, 10 | imports: [BullMQCoreModule.forRoot(options, connection)], 11 | exports: [BullMQCoreModule], 12 | }; 13 | } 14 | 15 | public static forRootAsync(options: BullMQModuleAsyncOptions, connection?: string): DynamicModule { 16 | return { 17 | module: BullMQModule, 18 | imports: [BullMQCoreModule.forRootAsync(options, connection)], 19 | exports: [BullMQCoreModule], 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bullmq.utils.ts: -------------------------------------------------------------------------------- 1 | import { BullMQModuleOptions, BullMQ } from "./bullmq.interfaces"; 2 | import { Queue, Worker, QueueEvents, QueueScheduler, Job } from 'bullmq'; 3 | import { 4 | BULLMQ_MODULE_CONNECTION, 5 | BULLMQ_MODULE_CONNECTION_TOKEN, 6 | BULLMQ_MODULE_OPTIONS_TOKEN 7 | } from './bullmq.constants'; 8 | 9 | export { Queue, Worker, QueueEvents, QueueScheduler } 10 | export * as bullmq from 'bullmq'; 11 | 12 | export function getBullMQOptionsToken(options: Partial, connection: string): string { 13 | const queueName = options?.name || connection || BULLMQ_MODULE_CONNECTION; 14 | return `${ queueName }_${ connection || queueName }_${ BULLMQ_MODULE_OPTIONS_TOKEN }`; 15 | } 16 | 17 | export function getBullMQConnectionToken(options: Partial, connection: string): string { 18 | const queueName = options?.name || connection || BULLMQ_MODULE_CONNECTION; 19 | return `${ queueName }_${ connection || queueName }_${ BULLMQ_MODULE_CONNECTION_TOKEN }`; 20 | } 21 | 22 | export function createBullMQConnection(options: BullMQModuleOptions, connection: string): BullMQ { 23 | const { config, name } = options; 24 | const queueName = name || connection || BULLMQ_MODULE_CONNECTION; 25 | 26 | if(config?.url) { 27 | const url = new URL(config.url); 28 | config.connection = { 29 | host: url.hostname, 30 | port: +url.port, 31 | password: url.password, 32 | } 33 | } 34 | 35 | return { 36 | process: (callback: (job: Job) => any): Worker => { 37 | return new Worker(queueName, callback, config); 38 | }, 39 | queue: new Queue(queueName, config), 40 | queueEvents: new QueueEvents(queueName, config), 41 | queueScheduler: new QueueScheduler(queueName, config), 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bullmq.module'; 2 | export * from './bullmq.decorators'; 3 | export * from './bullmq.interfaces'; 4 | export * from './bullmq.utils'; -------------------------------------------------------------------------------- /test/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as index from '../src/index'; 2 | 3 | describe('Index', () => { 4 | test('should return 10 exports', () => { 5 | expect(Object.keys(index)).toHaveLength(10); 6 | }); 7 | }); -------------------------------------------------------------------------------- /test/jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } -------------------------------------------------------------------------------- /test/module.spec.ts: -------------------------------------------------------------------------------- 1 | describe('Module', () => { 2 | it(`Instance BullMQ`, async () => { 3 | expect(1).toBe(1); 4 | }); 5 | }); 6 | 7 | // import { Injectable, INestApplication } from '@nestjs/common'; 8 | // import * as Redis from 'ioredis'; 9 | // import { Test, TestingModule } from '@nestjs/testing'; 10 | // import { BullMQModule, InjectBullMQ, BullMQ, } from '../src'; 11 | // import { Worker, Processor } from 'bullmq'; 12 | 13 | // describe('Module', () => { 14 | // it(`Instance BullMQ`, async () => { 15 | // let app: INestApplication = null; 16 | // let moduleRef: TestingModule = null; 17 | 18 | // @Injectable() 19 | // class BullMQService { 20 | // constructor(@InjectBullMQ() private readonly bullMq: BullMQ) {} 21 | 22 | // async getTest() { 23 | // this.bullMq.process(async job => { 24 | // console.log('process', job); 25 | // }) 26 | // await this.bullMq.queue.add('test', { hello: 'world' }); 27 | // return null; 28 | // } 29 | // } 30 | 31 | // moduleRef = await Test.createTestingModule({ 32 | // imports: [ 33 | // BullMQModule.forRootAsync({ 34 | // useFactory: () => ({ 35 | // name: 'QueueName', 36 | // config: { connection: { host: 'localhost', port: 6378 } }, 37 | // }), 38 | // }), 39 | // ], 40 | // providers: [BullMQService], 41 | // }).compile(); 42 | 43 | // app = moduleRef.createNestApplication(); 44 | 45 | // await app.init(); 46 | 47 | // const bullMQModule = moduleRef.get(BullMQModule); 48 | // const bullMQService = moduleRef.get(BullMQService); 49 | 50 | // await bullMQService.getTest(); 51 | // expect(bullMQModule).toBeInstanceOf(BullMQModule); 52 | 53 | // await new Promise((resolve) => { 54 | // setTimeout(() => { 55 | // resolve(); 56 | // }, 4000); 57 | // }) 58 | // await app.close(); 59 | // }); 60 | // }); -------------------------------------------------------------------------------- /test/module.test.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, INestApplication } from '@nestjs/common'; 2 | import { Test, TestingModule } from '@nestjs/testing'; 3 | import { BullMQModule, InjectBullMQ, BullMQ, } from '../src'; 4 | 5 | const run = async() => { 6 | let app: INestApplication = null; 7 | let moduleRef: TestingModule = null; 8 | 9 | @Injectable() 10 | class BullMQService { 11 | constructor(@InjectBullMQ() private readonly bullMq: BullMQ) {} 12 | 13 | async getTest() { 14 | this.bullMq.process(async job => { 15 | console.log('process', job); 16 | }) 17 | await this.bullMq.queue.add('test', { hello: 'world' }); 18 | return null; 19 | } 20 | } 21 | 22 | moduleRef = await Test.createTestingModule({ 23 | imports: [ 24 | BullMQModule.forRootAsync({ 25 | useFactory: () => ({ 26 | name: 'QueueName', 27 | config: { connection: { host: 'localhost', port: 6378 } }, 28 | }), 29 | }), 30 | ], 31 | providers: [BullMQService], 32 | }).compile(); 33 | 34 | app = moduleRef.createNestApplication(); 35 | 36 | await app.init(); 37 | 38 | const bullMQModule = moduleRef.get(BullMQModule); 39 | const bullMQService = moduleRef.get(BullMQService); 40 | 41 | await bullMQService.getTest(); 42 | console.log(bullMQModule.constructor.name); 43 | console.log(bullMQService.constructor.name); 44 | 45 | await app.close(); 46 | }; 47 | 48 | run().then(); 49 | 50 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": false, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "skipLibCheck": true 13 | }, 14 | "include": ["src/**/*", "test/**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | --------------------------------------------------------------------------------