diff --git a/packages/server-nest/package.json b/packages/server-nest/package.json index dd3d1c59d..1ed094b5b 100644 --- a/packages/server-nest/package.json +++ b/packages/server-nest/package.json @@ -24,7 +24,6 @@ "@aws-sdk/s3-request-presigner": "^3.583.0", "@bigcapital/email-components": "*", "@bigcapital/pdf-templates": "*", - "@bigcapital/server": "*", "@bigcapital/utils": "*", "@casl/ability": "^5.4.3", "@lemonsqueezy/lemonsqueezy.js": "^2.2.0", @@ -49,6 +48,7 @@ "async": "^3.2.0", "async-mutex": "^0.5.0", "axios": "^1.6.0", + "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", "bluebird": "^3.7.2", "bull": "^4.16.3", diff --git a/packages/server-nest/src/modules/Auth/Auth.controller.ts b/packages/server-nest/src/modules/Auth/Auth.controller.ts index 1e8af9b07..7efb51cda 100644 --- a/packages/server-nest/src/modules/Auth/Auth.controller.ts +++ b/packages/server-nest/src/modules/Auth/Auth.controller.ts @@ -1,25 +1,74 @@ -import { Body, Controller, Post, Request } from '@nestjs/common'; +import { Body, Controller, Param, Post, Request } from '@nestjs/common'; +import { ApiTags, ApiOperation, ApiBody, ApiParam } from '@nestjs/swagger'; import { PublicRoute } from './Jwt.guard'; import { AuthenticationApplication } from './AuthApplication.sevice'; import { AuthSignupDto } from './dtos/AuthSignup.dto'; import { AuthSigninDto } from './dtos/AuthSignin.dto'; +@ApiTags('Auth') @Controller('/auth') @PublicRoute() export class AuthController { constructor(private readonly authApp: AuthenticationApplication) {} + @Post('/signin') + @ApiOperation({ summary: 'Sign in a user' }) + @ApiBody({ type: AuthSigninDto }) signin(@Request() req: Request, @Body() signinDto: AuthSigninDto) { return this.authApp.signIn(signinDto); } @Post('/signup') + @ApiOperation({ summary: 'Sign up a new user' }) + @ApiBody({ type: AuthSignupDto }) signup(@Request() req: Request, @Body() signupDto: AuthSignupDto) { - this.authApp.signUp(signupDto); + return this.authApp.signUp(signupDto); } @Post('/signup/confirm') + @ApiOperation({ summary: 'Confirm user signup' }) + @ApiBody({ + schema: { + type: 'object', + properties: { + email: { type: 'string', example: 'user@example.com' }, + token: { type: 'string', example: 'confirmation-token' }, + }, + }, + }) signupConfirm(@Body('email') email: string, @Body('token') token: string) { return this.authApp.signUpConfirm(email, token); } + + @Post('/send_reset_password') + @ApiOperation({ summary: 'Send reset password email' }) + @ApiBody({ + schema: { + type: 'object', + properties: { + email: { type: 'string', example: 'user@example.com' }, + }, + }, + }) + sendResetPassword(@Body('email') email: string) { + return this.authApp.sendResetPassword(email); + } + + @Post('/reset_password/:token') + @ApiOperation({ summary: 'Reset password using token' }) + @ApiParam({ name: 'token', description: 'Reset password token' }) + @ApiBody({ + schema: { + type: 'object', + properties: { + password: { type: 'string', example: 'new-password' }, + }, + }, + }) + resetPassword( + @Param('token') token: string, + @Body('password') password: string, + ) { + return this.authApp.resetPassword(token, password); + } } diff --git a/packages/server-nest/src/modules/Auth/Auth.module.ts b/packages/server-nest/src/modules/Auth/Auth.module.ts index f500285ea..464fc5a00 100644 --- a/packages/server-nest/src/modules/Auth/Auth.module.ts +++ b/packages/server-nest/src/modules/Auth/Auth.module.ts @@ -10,6 +10,12 @@ import { AuthSignupConfirmResendService } from './commands/AuthSignupConfirmRese import { AuthSignupConfirmService } from './commands/AuthSignupConfirm.service'; import { AuthSignupService } from './commands/AuthSignup.service'; import { AuthSigninService } from './commands/AuthSignin.service'; +import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module'; +import { PasswordReset } from './models/PasswordReset'; +import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module'; +import { AuthenticationMailMesssages } from './AuthMailMessages.esrvice'; + +const models = [RegisterTenancyModel(PasswordReset)]; @Module({ controllers: [AuthController], @@ -18,7 +24,10 @@ import { AuthSigninService } from './commands/AuthSignin.service'; secret: 'asdfasdfasdf', signOptions: { expiresIn: '60s' }, }), + TenantDBManagerModule, + ...models, ], + exports: [...models], providers: [ AuthService, JwtStrategy, @@ -29,6 +38,7 @@ import { AuthSigninService } from './commands/AuthSignin.service'; AuthSignupConfirmService, AuthSignupService, AuthSigninService, + AuthenticationMailMesssages, ], }) export class AuthModule {} diff --git a/packages/server-nest/src/modules/Auth/AuthMailMessages.esrvice.ts b/packages/server-nest/src/modules/Auth/AuthMailMessages.esrvice.ts new file mode 100644 index 000000000..97b6a12e9 --- /dev/null +++ b/packages/server-nest/src/modules/Auth/AuthMailMessages.esrvice.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@nestjs/common'; +import { SystemUser } from '../System/models/SystemUser'; +import { ModelObject } from 'objection'; +import { ConfigService } from '@nestjs/config'; +import { Mail } from '../Mail/Mail'; + +@Injectable() +export class AuthenticationMailMesssages { + constructor(private readonly configService: ConfigService) {} + /** + * Sends reset password message. + * @param {ISystemUser} user - The system user. + * @param {string} token - Reset password token. + * @returns {Mail} + */ + sendResetPasswordMessage(user: ModelObject, token: string) { + const baseURL = this.configService.get('baseURL'); + + return new Mail() + .setSubject('Bigcapital - Password Reset') + .setView('mail/ResetPassword.html') + .setTo(user.email) + .setAttachments([ + { + filename: 'bigcapital.png', + path: `${global.__views_dir}/images/bigcapital.png`, + cid: 'bigcapital_logo', + }, + ]) + .setData({ + resetPasswordUrl: `${baseURL}/auth/reset_password/${token}`, + first_name: user.firstName, + last_name: user.lastName, + }); + } + + /** + * Sends signup verification mail. + * @param {string} email - Email address + * @param {string} fullName - User name. + * @param {string} token - Verification token. + * @returns {Mail} + */ + sendSignupVerificationMail(email: string, fullName: string, token: string) { + const baseURL = this.configService.get('baseURL'); + const verifyUrl = `${baseURL}/auth/email_confirmation?token=${token}&email=${email}`; + + return new Mail() + .setSubject('Bigcapital - Verify your email') + .setView('mail/SignupVerifyEmail.html') + .setTo(email) + .setAttachments([ + { + filename: 'bigcapital.png', + path: `${global.__views_dir}/images/bigcapital.png`, + cid: 'bigcapital_logo', + }, + ]) + .setData({ verifyUrl, fullName }); + } +} diff --git a/packages/server-nest/src/modules/Auth/commands/AuthResetPassword.service.ts b/packages/server-nest/src/modules/Auth/commands/AuthResetPassword.service.ts index a2f0e23cd..44e76ae89 100644 --- a/packages/server-nest/src/modules/Auth/commands/AuthResetPassword.service.ts +++ b/packages/server-nest/src/modules/Auth/commands/AuthResetPassword.service.ts @@ -1,10 +1,88 @@ -import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Inject, Injectable } from '@nestjs/common'; +import { SystemUser } from '@/modules/System/models/SystemUser'; +import { PasswordReset } from '../models/PasswordReset'; +import { ServiceError } from '@/modules/Items/ServiceError'; +import { ERRORS } from '../Auth.constants'; +import { hashPassword } from '../Auth.utils'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { events } from '@/common/events/events'; +import { IAuthResetedPasswordEventPayload } from '../Auth.interfaces'; @Injectable() export class AuthResetPasswordService { - resetPassword(token: string, password: string): Promise<{ message: string }> { - return new Promise((resolve) => { - resolve({ message: 'Reset password link sent to your email' }); - }); + /** + * @param {ConfigService} configService - Config service. + * @param {EventEmitter2} eventEmitter - Event emitter. + * @param {typeof SystemUser} systemUserModel + * @param {typeof PasswordReset} passwordResetModel - Reset password model. + */ + constructor( + private readonly configService: ConfigService, + private readonly eventEmitter: EventEmitter2, + + @Inject(SystemUser.name) + private readonly systemUserModel: typeof SystemUser, + + @Inject(PasswordReset.name) + private readonly passwordResetModel: typeof PasswordReset, + ) {} + + /** + * Resets a user password from given token. + * @param {string} token - Password reset token. + * @param {string} password - New Password. + * @return {Promise} + */ + public async resetPassword(token: string, password: string): Promise { + // Finds the password reset token. + const tokenModel = await this.passwordResetModel + .query() + .findOne('token', token); + + // In case the password reset token not found throw token invalid error.. + if (!tokenModel) { + throw new ServiceError(ERRORS.TOKEN_INVALID); + } + const resetPasswordSeconds = this.configService.get('resetPasswordSeconds'); + + // Different between tokne creation datetime and current time. + if (moment().diff(tokenModel.createdAt, 'seconds') > resetPasswordSeconds) { + // Deletes the expired token by expired token email. + await this.deletePasswordResetToken(tokenModel.email); + throw new ServiceError(ERRORS.TOKEN_EXPIRED); + } + const user = await this.systemUserModel + .query() + .findOne({ email: tokenModel.email }); + + if (!user) { + throw new ServiceError(ERRORS.USER_NOT_FOUND); + } + const hashedPassword = await hashPassword(password); + + await this.systemUserModel + .query() + .findById(user.id) + .update({ password: hashedPassword }); + + // Deletes the used token. + await this.deletePasswordResetToken(tokenModel.email); + + // Triggers `onResetPassword` event. + await this.eventEmitter.emitAsync(events.auth.resetPassword, { + user, + token, + password, + } as IAuthResetedPasswordEventPayload); + } + + /** + * Deletes the password reset token by the given email. + * @param {string} email + * @returns {Promise} + */ + private async deletePasswordResetToken(email: string) { + return PasswordReset.query().where('email', email).delete(); } } diff --git a/packages/server-nest/src/modules/Auth/commands/AuthSendResetPassword.service.ts b/packages/server-nest/src/modules/Auth/commands/AuthSendResetPassword.service.ts index ae657d1bc..90a6b3191 100644 --- a/packages/server-nest/src/modules/Auth/commands/AuthSendResetPassword.service.ts +++ b/packages/server-nest/src/modules/Auth/commands/AuthSendResetPassword.service.ts @@ -1,10 +1,70 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; +import * as uniqid from 'uniqid'; +import { + IAuthSendedResetPassword, + IAuthSendingResetPassword, +} from '../Auth.interfaces'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { PasswordReset } from '../models/PasswordReset'; +import { SystemUser } from '@/modules/System/models/SystemUser'; +import { events } from '@/common/events/events'; @Injectable() export class AuthSendResetPasswordService { - sendResetPassword(email: string): Promise<{ message: string }> { - return new Promise((resolve) => { - resolve({ message: 'Reset password link sent to your email' }); + /** + * @param {EventEmitter2} eventPublisher - Event emitter. + * @param {typeof PasswordReset} resetPasswordModel - Password reset model. + * @param {typeof SystemUser} systemUserModel - System user model. + */ + constructor( + private readonly eventPublisher: EventEmitter2, + + @Inject(PasswordReset.name) + private readonly resetPasswordModel: typeof PasswordReset, + + @Inject(SystemUser.name) + private readonly systemUserModel: typeof SystemUser, + ) {} + + /** + * Sends the given email reset password email. + * @param {string} email - Email address. + */ + async sendResetPassword(email: string): Promise { + const user = await this.systemUserModel + .query() + .findOne({ email }) + .throwIfNotFound(); + + const token: string = uniqid(); + + // Triggers sending reset password event. + await this.eventPublisher.emitAsync(events.auth.sendingResetPassword, { + user, + token, + } as IAuthSendingResetPassword); + + // Delete all stored tokens of reset password that associate to the give email. + this.deletePasswordResetToken(email); + + // Creates a new password reset row with unique token. + const passwordReset = await this.resetPasswordModel.query().insert({ + email, + token, }); + // Triggers sent reset password event. + await this.eventPublisher.emitAsync(events.auth.sendResetPassword, { + user, + token, + } as IAuthSendedResetPassword); + } + + /** + * Deletes the password reset token by the given email. + * @param {string} email + * @returns {Promise} + */ + private async deletePasswordResetToken(email: string) { + return this.resetPasswordModel.query().where('email', email).delete(); } } diff --git a/packages/server-nest/src/modules/Auth/commands/AuthSignup.service.ts b/packages/server-nest/src/modules/Auth/commands/AuthSignup.service.ts index 4a8a6a5e1..ba38fa0d6 100644 --- a/packages/server-nest/src/modules/Auth/commands/AuthSignup.service.ts +++ b/packages/server-nest/src/modules/Auth/commands/AuthSignup.service.ts @@ -1,4 +1,5 @@ -import crypto from 'crypto'; +import * as crypto from 'crypto'; +import * as moment from 'moment'; import { events } from '@/common/events/events'; import { ServiceError } from '@/modules/Items/ServiceError'; import { SystemUser } from '@/modules/System/models/SystemUser'; diff --git a/packages/server-nest/src/modules/Auth/models/PasswordReset.ts b/packages/server-nest/src/modules/Auth/models/PasswordReset.ts new file mode 100644 index 000000000..a996153a1 --- /dev/null +++ b/packages/server-nest/src/modules/Auth/models/PasswordReset.ts @@ -0,0 +1,22 @@ +import { SystemModel } from '@/modules/System/models/SystemModel'; + +export class PasswordReset extends SystemModel { + readonly email: string; + readonly token: string; + + readonly createdAt: Date; + + /** + * Table name + */ + static get tableName() { + return 'password_resets'; + } + + /** + * Timestamps columns. + */ + get timestamps() { + return ['createdAt']; + } +} diff --git a/packages/server-nest/src/modules/Auth/processors/SendResetPasswordMail.processor.ts b/packages/server-nest/src/modules/Auth/processors/SendResetPasswordMail.processor.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/server-nest/src/modules/Auth/processors/SendSignupVerificationMail.processor.ts b/packages/server-nest/src/modules/Auth/processors/SendSignupVerificationMail.processor.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/server-nest/src/modules/Subscription/models/PlanSubscription.ts b/packages/server-nest/src/modules/Subscription/models/PlanSubscription.ts index ff561ab78..85d65e0c2 100644 --- a/packages/server-nest/src/modules/Subscription/models/PlanSubscription.ts +++ b/packages/server-nest/src/modules/Subscription/models/PlanSubscription.ts @@ -103,8 +103,8 @@ export class PlanSubscription extends mixin(SystemModel) { * Relations mappings. */ static get relationMappings() { - const Tenant = require('system/models/Tenant'); - const Plan = require('system/models/Subscriptions/Plan'); + const { TenantModel } = require('../../System/models/TenantModel'); + const { Plan } = require('./Plan'); return { /** @@ -112,7 +112,7 @@ export class PlanSubscription extends mixin(SystemModel) { */ tenant: { relation: Model.BelongsToOneRelation, - modelClass: Tenant.default, + modelClass: TenantModel, join: { from: 'subscription_plan_subscriptions.tenantId', to: 'tenants.id', @@ -124,7 +124,7 @@ export class PlanSubscription extends mixin(SystemModel) { */ plan: { relation: Model.BelongsToOneRelation, - modelClass: Plan.default, + modelClass: Plan, join: { from: 'subscription_plan_subscriptions.planId', to: 'subscription_plans.id', diff --git a/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts b/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts index 92320fcda..328a6df53 100644 --- a/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts +++ b/packages/server-nest/src/modules/System/repositories/Tenant.repository.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { Knex } from 'knex'; -import { Global, Inject, Injectable } from '@nestjs/common'; -import uniqid from 'uniqid'; +import * as uniqid from 'uniqid'; import { TenantRepository as TenantBaseRepository } from '@/common/repository/TenantRepository'; import { SystemKnexConnection } from '../SystemDB/SystemDB.constants'; import { TenantModel } from '../models/TenantModel'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 676423c4f..be2b0bf77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -496,9 +496,6 @@ importers: '@bigcapital/pdf-templates': specifier: '*' version: link:../../shared/pdf-templates - '@bigcapital/server': - specifier: '*' - version: link:../server '@bigcapital/utils': specifier: '*' version: link:../../shared/bigcapital-utils @@ -571,6 +568,9 @@ importers: axios: specifier: ^1.6.0 version: 1.7.7 + bcrypt: + specifier: ^5.1.1 + version: 5.1.1 bcryptjs: specifier: ^2.4.3 version: 2.4.3 @@ -7229,6 +7229,24 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + /@mapbox/node-pre-gyp@1.0.11: + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.6.7 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@mdx-js/react@2.3.0(react@18.3.1): resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: @@ -7751,7 +7769,7 @@ packages: node-gyp: 10.1.0 node-gyp-build: 4.8.1 prebuildify: 6.0.1 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: false @@ -7786,7 +7804,7 @@ packages: pretty-bytes: 5.6.0 request-ip: 3.3.0 ringbufferjs: 2.0.0 - semver: 7.6.2 + semver: 7.6.3 sync-request: 6.1.0 unescape: 1.0.1 unescape-js: 1.1.4 @@ -7844,7 +7862,7 @@ packages: resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - semver: 7.6.2 + semver: 7.6.3 /@npmcli/git@5.0.7: resolution: {integrity: sha512-WaOVvto604d5IpdCRV2KjQu8PzkfE96d50CQGKgywXh2GxXmDeUO5EWcBC4V57uFyrNqx83+MewuJh3WTR3xPA==} @@ -7856,7 +7874,7 @@ packages: proc-log: 4.2.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.6.2 + semver: 7.6.3 which: 4.0.0 transitivePeerDependencies: - bluebird @@ -10744,7 +10762,7 @@ packages: prompts: 2.4.2 puppeteer-core: 2.1.1 read-pkg-up: 7.0.1 - semver: 7.6.2 + semver: 7.6.3 simple-update-notifier: 2.0.0 strip-json-comments: 3.1.1 tempy: 1.0.1 @@ -10962,7 +10980,7 @@ packages: pretty-hrtime: 1.0.3 prompts: 2.4.2 read-pkg-up: 7.0.1 - semver: 7.6.2 + semver: 7.6.3 serve-favicon: 2.5.0 telejson: 7.2.0 tiny-invariant: 1.3.3 @@ -11078,7 +11096,7 @@ packages: memoizerific: 1.11.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - semver: 7.6.2 + semver: 7.6.3 store2: 2.14.3 telejson: 7.2.0 ts-dedent: 2.2.0 @@ -12876,7 +12894,7 @@ packages: graphemer: 1.4.0 ignore: 5.3.1 natural-compare-lite: 1.4.0 - semver: 7.6.2 + semver: 7.6.3 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: @@ -13117,7 +13135,7 @@ packages: glob: 7.2.3 is-glob: 4.0.3 lodash: 4.17.21 - semver: 7.6.2 + semver: 7.6.3 tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: @@ -13223,7 +13241,7 @@ packages: '@typescript-eslint/typescript-estree': 5.62.0(typescript@3.9.10) eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -13243,7 +13261,7 @@ packages: '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -13263,7 +13281,7 @@ packages: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3) eslint: 9.13.0 eslint-scope: 5.1.1 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -13751,6 +13769,10 @@ packages: deprecated: Use your platform's native atob() and btoa() methods instead dev: false + /abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: false + /abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -14190,11 +14212,19 @@ packages: /aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: true /archy@1.0.0: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + /are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + /are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -15146,6 +15176,18 @@ packages: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} dev: false + /bcrypt@5.1.1: + resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /bcryptjs@2.4.3: resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} dev: false @@ -15580,7 +15622,7 @@ packages: /builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} dependencies: - semver: 7.6.2 + semver: 7.6.3 dev: true /bull@4.16.4: @@ -16526,7 +16568,6 @@ packages: /console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: true /constant-case@3.0.4: resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} @@ -16615,7 +16656,7 @@ packages: handlebars: 4.7.8 json-stringify-safe: 5.0.1 meow: 8.1.2 - semver: 7.6.2 + semver: 7.6.3 split: 1.0.1 dev: true @@ -17092,7 +17133,7 @@ packages: postcss-modules-scope: 3.2.0(postcss@8.4.47) postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.91.0(esbuild@0.23.1)(webpack-cli@4.10.0) /css-loader@6.11.0(webpack@5.96.1): @@ -17114,7 +17155,7 @@ packages: postcss-modules-scope: 3.2.0(postcss@8.4.47) postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.96.1(esbuild@0.18.20) dev: true @@ -17702,7 +17743,6 @@ packages: /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true /denque@1.5.1: resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==} @@ -17767,7 +17807,6 @@ packages: engines: {node: '>=8'} requiresBuild: true dev: false - optional: true /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} @@ -18123,7 +18162,7 @@ packages: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.1 - semver: 7.6.2 + semver: 7.6.3 dev: false /ee-first@1.1.1: @@ -20232,7 +20271,7 @@ packages: memfs: 3.5.3 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.6.2 + semver: 7.6.3 tapable: 1.1.3 typescript: 4.9.5 webpack: 5.91.0(esbuild@0.23.1)(webpack-cli@4.10.0) @@ -20504,6 +20543,22 @@ packages: /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + /gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + /gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -20702,7 +20757,7 @@ packages: hasBin: true dependencies: meow: 8.1.2 - semver: 7.6.2 + semver: 7.6.3 dev: true /git-up@7.0.0: @@ -21192,7 +21247,6 @@ packages: /has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: true /has-value@0.3.1: resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} @@ -22664,7 +22718,7 @@ packages: '@babel/parser': 7.26.1 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: true @@ -23487,7 +23541,7 @@ packages: jest-util: 27.5.1 natural-compare: 1.4.0 pretty-format: 27.5.1 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: false @@ -23515,7 +23569,7 @@ packages: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: true @@ -26117,13 +26171,17 @@ packages: engines: {node: '>=10'} requiresBuild: true dependencies: - semver: 7.6.2 + semver: 7.6.3 dev: false optional: true /node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + /node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + dev: false + /node-cache@4.2.1: resolution: {integrity: sha512-BOb67bWg2dTyax5kdef5WfU3X8xu4wPg+zHzkvls0Q/QpYycIFRLEEIdAx9Wma43DxG6Qzn4illdZoYseKWa4A==} engines: {node: '>= 0.4.6'} @@ -26198,7 +26256,7 @@ packages: make-fetch-happen: 13.0.1 nopt: 7.2.1 proc-log: 3.0.0 - semver: 7.6.2 + semver: 7.6.3 tar: 6.2.1 which: 4.0.0 transitivePeerDependencies: @@ -26272,6 +26330,14 @@ packages: update-notifier: 2.5.0 dev: false + /nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + /nopt@7.2.1: resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -26293,7 +26359,7 @@ packages: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.13.1 - semver: 7.6.2 + semver: 7.6.3 validate-npm-package-license: 3.0.4 dev: true @@ -26303,7 +26369,7 @@ packages: dependencies: hosted-git-info: 6.1.1 is-core-module: 2.13.1 - semver: 7.6.2 + semver: 7.6.3 validate-npm-package-license: 3.0.4 dev: true @@ -26313,7 +26379,7 @@ packages: dependencies: hosted-git-info: 7.0.2 is-core-module: 2.13.1 - semver: 7.6.2 + semver: 7.6.3 validate-npm-package-license: 3.0.4 dev: true @@ -26366,7 +26432,7 @@ packages: resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - semver: 7.6.2 + semver: 7.6.3 dev: true /npm-normalize-package-bin@1.0.1: @@ -26384,7 +26450,7 @@ packages: dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 - semver: 7.6.2 + semver: 7.6.3 validate-npm-package-name: 5.0.0 dev: true @@ -26394,7 +26460,7 @@ packages: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 validate-npm-package-name: 5.0.0 dev: true @@ -26432,7 +26498,7 @@ packages: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 11.0.2 - semver: 7.6.2 + semver: 7.6.3 dev: true /npm-registry-fetch@14.0.5: @@ -26511,6 +26577,16 @@ packages: path-key: 4.0.0 dev: true + /npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: false + /npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -28130,7 +28206,7 @@ packages: cosmiconfig: 7.1.0 klona: 2.0.6 postcss: 8.4.38 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.91.0(esbuild@0.23.1)(webpack-cli@4.10.0) dev: false @@ -28144,7 +28220,7 @@ packages: cosmiconfig: 8.3.6(typescript@5.6.3) jiti: 1.21.0 postcss: 8.4.47 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.91.0(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - typescript @@ -28160,7 +28236,7 @@ packages: cosmiconfig: 8.3.6(typescript@5.6.3) jiti: 1.21.0 postcss: 8.4.47 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.96.1(esbuild@0.18.20) transitivePeerDependencies: - typescript @@ -31417,7 +31493,6 @@ packages: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true - dev: true /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -31703,7 +31778,7 @@ packages: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} dependencies: - semver: 7.6.2 + semver: 7.6.3 dev: true /sinon@7.5.0: @@ -35235,7 +35310,6 @@ packages: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: string-width: 4.2.3 - dev: true /widest-line@2.0.1: resolution: {integrity: sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==}