From 8b58e65ed6852173f1a695e4ed8ac238ae4069e5 Mon Sep 17 00:00:00 2001 From: lukestahl Date: Tue, 24 Oct 2023 11:36:24 +0200 Subject: [PATCH] add eslint-config, prettier-config & tsconfig package to uniform code style --- .github/workflows/{lint-api.yml => lint.yml} | 2 +- .prettierrc | 7 - apps/api/.eslintrc.js | 25 - apps/api/.prettierrc | 4 - apps/api/package.json | 23 +- apps/api/src/app.controller.spec.ts | 14 +- apps/api/src/app.controller.ts | 79 +- apps/api/src/app.module.ts | 28 +- apps/api/src/app.service.ts | 4 +- apps/api/src/auth/auth.controller.ts | 175 ++- apps/api/src/auth/auth.middleware.ts | 10 +- apps/api/src/auth/auth.module.ts | 29 +- apps/api/src/auth/config.interface.ts | 8 +- apps/api/src/auth/filter/auth.filter.spec.ts | 6 +- apps/api/src/auth/filter/auth.filter.ts | 18 +- apps/api/src/auth/filter/auth.guard.spec.ts | 6 +- apps/api/src/auth/filter/auth.guard.ts | 21 +- .../api/src/auth/session/session.decorator.ts | 8 - .../supertokens/supertokens.service.spec.ts | 10 +- .../auth/supertokens/supertokens.service.ts | 187 ++- apps/api/src/email/email.service.ts | 42 +- apps/api/src/logger/logger.middleware.ts | 17 +- apps/api/src/main.ts | 39 +- apps/api/src/user/user.controller.spec.ts | 12 +- apps/api/src/user/user.controller.ts | 9 +- apps/api/src/user/user.module.ts | 7 +- apps/api/src/user/user.service.spec.ts | 10 +- apps/api/src/user/user.service.ts | 2 +- apps/api/test/app.e2e-spec.ts | 19 +- apps/api/tsconfig.json | 4 +- apps/expo/app.config.ts | 54 +- apps/expo/app/(app)/(authorized)/_layout.tsx | 30 +- .../(app)/(authorized)/account/_layout.tsx | 1 + .../account/delete-account/modal.tsx | 44 +- .../(authorized)/account/email/change.tsx | 34 +- .../app/(app)/(authorized)/account/index.tsx | 141 +- .../(authorized)/account/password/change.tsx | 69 +- .../(app)/(authorized)/account/user/edit.tsx | 77 +- .../app/(app)/(authorized)/home/index.tsx | 32 +- apps/expo/app/(app)/auth/_layout.tsx | 4 +- .../(app)/auth/email/verify-sent/index.tsx | 9 +- .../(app)/auth/password/reset-sent/index.tsx | 9 +- .../app/(app)/auth/password/reset/index.tsx | 41 +- apps/expo/app/(app)/auth/signin/index.tsx | 128 +- apps/expo/app/(app)/auth/signup/index.tsx | 129 +- apps/expo/app/+html.tsx | 2 +- apps/expo/app/[...missing].tsx | 5 +- apps/expo/app/_layout.tsx | 42 +- apps/expo/babel.config.js | 22 +- apps/expo/components/auth/apple.button.tsx | 7 +- apps/expo/components/auth/auth.error.ts | 2 +- apps/expo/components/auth/auth.provider.tsx | 61 +- apps/expo/components/auth/auth.service.ts | 28 +- apps/expo/components/auth/auth.store.ts | 31 +- apps/expo/components/auth/form/interfaces.ts | 7 +- apps/expo/components/auth/github.button.tsx | 59 +- apps/expo/components/auth/google.button.tsx | 7 +- apps/expo/components/auth/socialproviders.tsx | 3 +- apps/expo/components/clients/rest.client.ts | 27 +- apps/expo/components/keyboard.tsx | 11 +- apps/expo/components/translate.ts | 7 +- apps/expo/i18next.d.ts | 3 +- apps/expo/metro.config.js | 24 +- apps/expo/package.json | 14 +- apps/expo/supertoken.config.ts | 1 + apps/expo/tamagui.config.ts | 2 +- apps/expo/tsconfig.json | 11 +- package.json | 3 +- packages/eslint-config/base.js | 33 + packages/eslint-config/package.json | 39 + packages/eslint-config/react.js | 20 + packages/eslint-config/tsconfig.json | 8 + packages/prettier-config/config.mjs | 30 + packages/prettier-config/package.json | 20 + packages/prettier-config/tsconfig.json | 8 + packages/tsconfig/base.json | 22 + packages/tsconfig/package.json | 8 + pnpm-lock.yaml | 1190 ++++++++++++++--- tsconfig.base.json | 35 - tsconfig.json | 12 - 80 files changed, 1998 insertions(+), 1433 deletions(-) rename .github/workflows/{lint-api.yml => lint.yml} (96%) delete mode 100644 .prettierrc delete mode 100644 apps/api/.eslintrc.js delete mode 100644 apps/api/.prettierrc delete mode 100644 apps/api/src/auth/session/session.decorator.ts create mode 100644 packages/eslint-config/base.js create mode 100644 packages/eslint-config/package.json create mode 100644 packages/eslint-config/react.js create mode 100644 packages/eslint-config/tsconfig.json create mode 100644 packages/prettier-config/config.mjs create mode 100644 packages/prettier-config/package.json create mode 100644 packages/prettier-config/tsconfig.json create mode 100644 packages/tsconfig/base.json create mode 100644 packages/tsconfig/package.json delete mode 100644 tsconfig.base.json delete mode 100644 tsconfig.json diff --git a/.github/workflows/lint-api.yml b/.github/workflows/lint.yml similarity index 96% rename from .github/workflows/lint-api.yml rename to .github/workflows/lint.yml index 9911fe3..7adaadb 100644 --- a/.github/workflows/lint-api.yml +++ b/.github/workflows/lint.yml @@ -52,4 +52,4 @@ jobs: run: pnpm install - name: Run linters - run: 'pnpm run lint' + run: "pnpm run lint && pnpm run format" diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 991770a..0000000 --- a/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "trailingComma": "es5", - "semi": false, - "singleQuote": true, - "arrowParens": "always", - "printWidth": 100 -} diff --git a/apps/api/.eslintrc.js b/apps/api/.eslintrc.js deleted file mode 100644 index 259de13..0000000 --- a/apps/api/.eslintrc.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - }, -}; diff --git a/apps/api/.prettierrc b/apps/api/.prettierrc deleted file mode 100644 index dcb7279..0000000 --- a/apps/api/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index d205807..c3bffb0 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -7,12 +7,12 @@ "license": "UNLICENSED", "scripts": { "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "format": "prettier --write \"**/*.{js,cjs,mjs,ts,tsx,md,json}\"", "start": "nest start", "dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "lint": "eslint . --fix", "test": "jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", @@ -36,6 +36,9 @@ "supertokens-node": "^16.3.3" }, "devDependencies": { + "@acme/eslint-config": "^0.2.0", + "@acme/prettier-config": "^0.1.0", + "@acme/tsconfig": "^0.1.0", "@nestjs/cli": "^10.2.0", "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.2.7", @@ -43,13 +46,8 @@ "@types/jest": "^29.5.6", "@types/node": "^20.8.7", "@types/supertest": "^2.0.15", - "@typescript-eslint/eslint-plugin": "^6.8.0", - "@typescript-eslint/parser": "^6.8.0", - "eslint": "^8.52.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.1", + "expo": "link:apps/expo", "jest": "^29.7.0", - "prettier": "^3.0.3", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.1", @@ -74,5 +72,12 @@ ], "coverageDirectory": "../coverage", "testEnvironment": "node" - } + }, + "eslintConfig": { + "root": true, + "extends": [ + "@acme/eslint-config/base" + ] + }, + "prettier": "@acme/prettier-config" } diff --git a/apps/api/src/app.controller.spec.ts b/apps/api/src/app.controller.spec.ts index 8ea0b07..db121f3 100644 --- a/apps/api/src/app.controller.spec.ts +++ b/apps/api/src/app.controller.spec.ts @@ -1,8 +1,10 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import type { TestingModule } from "@nestjs/testing"; +import { Test } from "@nestjs/testing"; -describe('AppController', () => { +import { AppController } from "./app.controller"; +import { AppService } from "./app.service"; + +describe("AppController", () => { let appController: AppController; beforeEach(async () => { @@ -14,9 +16,9 @@ describe('AppController', () => { appController = app.get(AppController); }); - describe('root', () => { + describe("root", () => { it('should return "Hello World!"', () => { - expect(appController.getTest(undefined)).toBe('Unauthorised'); + expect(appController.getTest(undefined)).toBe("Unauthorised"); }); }); }); diff --git a/apps/api/src/app.controller.ts b/apps/api/src/app.controller.ts index 7d401d6..7889077 100644 --- a/apps/api/src/app.controller.ts +++ b/apps/api/src/app.controller.ts @@ -1,73 +1,52 @@ -import { - Controller, - Delete, - Get, - Post, - Session, - UseGuards, -} from '@nestjs/common'; -import { EmailVerificationClaim } from 'supertokens-node/recipe/emailverification'; -import { - SessionClaimValidator, - SessionContainer, -} from 'supertokens-node/recipe/session'; -import UserRoles, { - PermissionClaim, - UserRoleClaim, -} from 'supertokens-node/recipe/userroles'; -import { AuthGuard } from './auth/filter/auth.guard'; +import { Controller, Delete, Get, Post, Session, UseGuards } from "@nestjs/common"; +import { EmailVerificationClaim } from "supertokens-node/recipe/emailverification"; +import type { SessionClaimValidator } from "supertokens-node/recipe/session"; +import { SessionContainer } from "supertokens-node/recipe/session"; +import UserRoles, { PermissionClaim, UserRoleClaim } from "supertokens-node/recipe/userroles"; + +import { AuthGuard } from "./auth/filter/auth.guard"; @Controller() export class AppController { - @Get('test') + @Get("test") @UseGuards( new AuthGuard({ - overrideGlobalClaimValidators: async ( - globalValidators: SessionClaimValidator[], - ) => [ + overrideGlobalClaimValidators: (globalValidators: SessionClaimValidator[]) => [ ...globalValidators, EmailVerificationClaim.validators.isVerified(), ], - }), + }) ) - async getTest(@Session() session: SessionContainer): Promise { + getTest(@Session() session: SessionContainer): string { return session.getUserId(); } - @Get('admin') + @Get("admin") @UseGuards( new AuthGuard({ - overrideGlobalClaimValidators: async ( - globalValidators: SessionClaimValidator[], - ) => [ + overrideGlobalClaimValidators: (globalValidators: SessionClaimValidator[]) => [ ...globalValidators, - UserRoles.UserRoleClaim.validators.includes('admin'), + UserRoles.UserRoleClaim.validators.includes("admin"), ], - }), + }) ) - async admin(): Promise { + admin(): string { return "you're an admin"; } // totally insecure route, just for demo purposes - @Post('make-me-an-admin') + @Post("make-me-an-admin") @UseGuards( new AuthGuard({ - overrideGlobalClaimValidators: async ( - globalValidators: SessionClaimValidator[], - ) => [ + overrideGlobalClaimValidators: (globalValidators: SessionClaimValidator[]) => [ ...globalValidators, EmailVerificationClaim.validators.isVerified(), ], - }), + }) ) async setRole(@Session() session: SessionContainer) { - await UserRoles.createNewRoleOrAddPermissions('admin', ['read', 'write']); - const response = await UserRoles.addRoleToUser( - session.getTenantId(), - session.getUserId(), - 'admin', - ); + await UserRoles.createNewRoleOrAddPermissions("admin", ["read", "write"]); + const response = await UserRoles.addRoleToUser(session.getTenantId(), session.getUserId(), "admin"); await session.fetchAndSetClaim(UserRoleClaim); await session.fetchAndSetClaim(PermissionClaim); @@ -75,23 +54,17 @@ export class AppController { } // totally insecure route, just for demo purposes - @Delete('remove-admin') + @Delete("remove-admin") @UseGuards( new AuthGuard({ - overrideGlobalClaimValidators: async ( - globalValidators: SessionClaimValidator[], - ) => [ + overrideGlobalClaimValidators: (globalValidators: SessionClaimValidator[]) => [ ...globalValidators, - UserRoles.UserRoleClaim.validators.includes('admin'), + UserRoles.UserRoleClaim.validators.includes("admin"), ], - }), + }) ) async removeAdmin(@Session() session: SessionContainer) { - const response = await UserRoles.removeUserRole( - session.getTenantId(), - session.getUserId(), - 'admin', - ); + const response = await UserRoles.removeUserRole(session.getTenantId(), session.getUserId(), "admin"); await session.fetchAndSetClaim(UserRoleClaim); await session.fetchAndSetClaim(PermissionClaim); return response; diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 902cf09..a89db54 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -1,10 +1,12 @@ -import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { AuthModule } from './auth/auth.module'; -import { LoggerMiddleware } from './logger/logger.middleware'; -import { UserModule } from './user/user.module'; +import type { MiddlewareConsumer, NestModule } from "@nestjs/common"; +import { Module } from "@nestjs/common"; +import { ConfigModule } from "@nestjs/config"; + +import { AppController } from "./app.controller"; +import { AppService } from "./app.service"; +import { AuthModule } from "./auth/auth.module"; +import { LoggerMiddleware } from "./logger/logger.middleware"; +import { UserModule } from "./user/user.module"; @Module({ imports: [ @@ -13,15 +15,15 @@ import { UserModule } from './user/user.module'; isGlobal: true, }), AuthModule.forRoot({ - connectionURI: process.env.SUPERTOKENS_CONNECTION_URI, + connectionURI: process.env.SUPERTOKENS_CONNECTION_URI ?? "", apiKey: process.env.SUPERTOKENS_API_KEY, appInfo: { - appName: 'SuperTokens Managed Service', - apiDomain: process.env.API_DOMAIN, + appName: "SuperTokens Managed Service", + apiDomain: process.env.API_DOMAIN ?? "", // only used for email links websiteDomain: process.env.API_DOMAIN, - apiBasePath: '/auth', - websiteBasePath: '/auth', + apiBasePath: "/auth", + websiteBasePath: "/auth", }, }), UserModule, @@ -31,6 +33,6 @@ import { UserModule } from './user/user.module'; }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { - consumer.apply(LoggerMiddleware).forRoutes('*'); + consumer.apply(LoggerMiddleware).forRoutes("*"); } } diff --git a/apps/api/src/app.service.ts b/apps/api/src/app.service.ts index 927d7cc..b00a667 100644 --- a/apps/api/src/app.service.ts +++ b/apps/api/src/app.service.ts @@ -1,8 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable } from "@nestjs/common"; @Injectable() export class AppService { getHello(): string { - return 'Hello World!'; + return "Hello World!"; } } diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index 8e7ddb9..8ecdb71 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -11,31 +11,27 @@ import { Session, UnauthorizedException, UseGuards, -} from '@nestjs/common'; -import EmailVerification from 'supertokens-node/recipe/emailverification'; -import SessionService from 'supertokens-node/recipe/session'; -import ThirdPartyEmailPassword from 'supertokens-node/recipe/thirdpartyemailpassword'; -import UserRoles from 'supertokens-node/recipe/userroles'; -import { AuthGuard } from './filter/auth.guard'; -import { - getUserMetadata, - updateUserMetadata, - clearUserMetadata, -} from 'supertokens-node/recipe/usermetadata'; -import { SessionContainer } from 'supertokens-node/recipe/session'; -import { JwtService } from '@nestjs/jwt'; -import supertokens, { deleteUser } from 'supertokens-node'; -import { EmailService } from '../email/email.service'; +} from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; +import supertokens, { deleteUser } from "supertokens-node"; +import EmailVerification from "supertokens-node/recipe/emailverification"; +import SessionService, { SessionContainer } from "supertokens-node/recipe/session"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import { clearUserMetadata, getUserMetadata, updateUserMetadata } from "supertokens-node/recipe/usermetadata"; +import UserRoles from "supertokens-node/recipe/userroles"; + +import { EmailService } from "../email/email.service"; +import { AuthGuard } from "./filter/auth.guard"; @Controller() export class AuthController { constructor( private readonly jwtService: JwtService, - private readonly emailService: EmailService, + private readonly emailService: EmailService ) {} @UseGuards(new AuthGuard()) - @Get('/auth/session') + @Get("/auth/session") @HttpCode(200) async getSession(@Session() session: SessionContainer) { const [metaDataResponse, user, roles] = await Promise.all([ @@ -47,9 +43,9 @@ export class AuthController { throw new UnauthorizedException(); } return { - metadata: { ...metaDataResponse.metadata, email: user.emails[0] }, + metadata: { ...(metaDataResponse.metadata as object), email: user.emails[0] }, session: { - thirdParty: user.thirdParty ?? null, + thirdParty: user.thirdParty, userId: session.getUserId(), }, roles: roles.roles, @@ -57,46 +53,39 @@ export class AuthController { } @UseGuards(new AuthGuard()) - @Patch('/auth/user') + @Patch("/auth/user") async updateUser( @Session() session: SessionContainer, @Body() body: { formFields: { id: string; value: string }[]; - }, + } ) { const { formFields } = body; - const allowedFormFields = ['first_name', 'last_name', 'avatarUrl']; + const allowedFormFields = ["first_name", "last_name", "avatarUrl"]; const metadata = {}; for (const formField of formFields) { if (allowedFormFields.includes(formField.id)) { metadata[formField.id] = formField.value; } } - const avatarUrl = formFields.find( - (field) => field.id === 'avatarUrl', - ).value; + const avatarUrl = formFields.find((field) => field.id === "avatarUrl")?.value; if (avatarUrl) { - const buffer = Buffer.from( - avatarUrl.substring(avatarUrl.indexOf(',') + 1), - ); - console.log('Image Upload in MB: ' + buffer.length / 1e6); + const buffer = Buffer.from(avatarUrl.substring(avatarUrl.indexOf(",") + 1)); + console.log("Image Upload in MB: " + buffer.length / 1e6); } return await updateUserMetadata(session.getUserId(), metadata); } - @Get('/auth/verify-email') - @Render('auth/verify-email/index.hbs') + @Get("/auth/verify-email") + @Render("auth/verify-email/index.hbs") async verifyEmail(@Query() query) { - const { token, rid, tenantId } = query; - if (!token || !rid || !tenantId || rid !== 'emailverification') { + const { token, rid, tenantId } = query as { token: string; rid: string; tenantId: string }; + if (!token || !rid || !tenantId || rid !== "emailverification") { throw new BadRequestException(); } - const response = await EmailVerification.verifyEmailUsingToken( - tenantId, - token, - ); - if (response.status === 'OK') { + const response = await EmailVerification.verifyEmailUsingToken(tenantId, token); + if (response.status === "OK") { await ThirdPartyEmailPassword.updateEmailOrPassword({ recipeUserId: response.user.recipeUserId, email: response.user.email, @@ -106,17 +95,17 @@ export class AuthController { return {}; } - @Get('/auth/reset-password') - @Render('auth/reset-password/index.hbs') + @Get("/auth/reset-password") + @Render("auth/reset-password/index.hbs") resetPasswort() { return {}; } - @Post('/auth/change-password') + @Post("/auth/change-password") @UseGuards(new AuthGuard()) async changePassword( @Session() session: SessionContainer, - @Body() body: { oldPassword: string; newPassword: string }, + @Body() body: { oldPassword: string; newPassword: string } ) { const { oldPassword, newPassword } = body; const userId = session.getUserId(); @@ -124,91 +113,80 @@ export class AuthController { const userInfo = await supertokens.getUser(userId); if (userInfo === undefined) { - throw new Error('Should never come here'); + throw new Error("Should never come here"); } - const passwordValidResponse = - await ThirdPartyEmailPassword.emailPasswordSignIn( - session.getTenantId(), - userInfo.emails[0], - oldPassword, - ); + const passwordValidResponse = await ThirdPartyEmailPassword.emailPasswordSignIn( + session.getTenantId(), + userInfo.emails[0]!, + oldPassword + ); - if (passwordValidResponse.status !== 'OK') { + if (passwordValidResponse.status !== "OK") { return passwordValidResponse; } const response = await ThirdPartyEmailPassword.updateEmailOrPassword({ recipeUserId: session.getRecipeUserId(), password: newPassword, - tenantIdForPasswordPolicy: session!.getTenantId(), + tenantIdForPasswordPolicy: session.getTenantId(), }); - if (response.status !== 'OK') { + if (response.status !== "OK") { return response; } const sessionId = session.getHandle(); - const sessionHandles = - await SessionService.getAllSessionHandlesForUser(userId); - const sessionHandlesToRevoke = sessionHandles.filter( - (handle) => handle !== sessionId, - ); + const sessionHandles = await SessionService.getAllSessionHandlesForUser(userId); + const sessionHandlesToRevoke = sessionHandles.filter((handle) => handle !== sessionId); await SessionService.revokeMultipleSessions(sessionHandlesToRevoke); return response; } - @Post('/auth/change-email') + @Post("/auth/change-email") @UseGuards(new AuthGuard()) - async changeMail( - @Session() session: SessionContainer, - @Body() body: { email: string }, - ) { + async changeMail(@Session() session: SessionContainer, @Body() body: { email: string }) { const { email } = body; if (!isValidEmail(email)) { return { - status: 'FIELD_ERROR', + status: "FIELD_ERROR", formFields: [ { - id: 'email', - error: 'Please enter a valid email address', + id: "email", + error: "Please enter a valid email address", }, ], }; } const userId = session.getUserId(); - const userAccount = await supertokens.getUser(userId!); - if (userAccount.thirdParty !== undefined) { + const userAccount = await supertokens.getUser(userId); + if (userAccount?.thirdParty) { return { - status: 'GENERAL_ERROR', - message: 'You are not allowed to change your email address', + status: "GENERAL_ERROR", + message: "You are not allowed to change your email address", }; } - const isVerified = await EmailVerification.isEmailVerified( - session.getRecipeUserId(), - email, - ); + const isVerified = await EmailVerification.isEmailVerified(session.getRecipeUserId(), email); if (!isVerified) { const user = await supertokens.getUser(session.getUserId()); - for (const tenantId of user.tenantIds) { - // Since once user can be shared across many tenants, we need to check if - // the email already exists in any of the tenants. - const usersWithEmail = await supertokens.listUsersByAccountInfo( - tenantId, - { + if (user) { + for (const tenantId of user.tenantIds) { + // Since once user can be shared across many tenants, we need to check if + // the email already exists in any of the tenants. + const usersWithEmail = await supertokens.listUsersByAccountInfo(tenantId, { email, - }, - ); - for (const userWithEmail of usersWithEmail) { - if (userWithEmail.id !== session.getUserId()) { - // TODO handle error, email already exists with another user. - return { - status: 'EMAIL_ALREADY_EXISTS_ERROR', - message: 'Email already exists with another user', - }; + }); + for (const userWithEmail of usersWithEmail) { + if (userWithEmail.id !== session.getUserId()) { + // TODO handle error, email already exists with another user. + return { + status: "EMAIL_ALREADY_EXISTS_ERROR", + message: "Email already exists with another user", + }; + } } } } @@ -216,7 +194,7 @@ export class AuthController { session.getTenantId(), session.getUserId(), session.getRecipeUserId(), - email, + email ); return response; } @@ -230,7 +208,7 @@ export class AuthController { } @UseGuards(new AuthGuard()) - @Post('/auth/delete-account') + @Post("/auth/delete-account") @HttpCode(200) async deleteAccount(@Session() session: SessionContainer) { const userId = session.getUserId(); @@ -240,7 +218,7 @@ export class AuthController { }; const token = this.jwtService.sign(jwtPayload, { secret: process.env.ACCOUNT_JWT_SECRET, - expiresIn: '15m', + expiresIn: "15m", }); const response = await supertokens.getUser(userId); @@ -250,19 +228,19 @@ export class AuthController { const { emails } = response; const deleteUrl = `${process.env.API_DOMAIN}/auth/delete-account/verify?token=${token}`; await this.emailService.sendAccountDeletionMail({ - email: emails[0], + email: emails[0]!, accountDeletionLink: deleteUrl, }); return { - status: 'OK', + status: "OK", }; } - @Get('/auth/delete-account/verify') - @Render('auth/delete-account/index.hbs') + @Get("/auth/delete-account/verify") + @Render("auth/delete-account/index.hbs") @HttpCode(200) async deleteAccountVerify(@Query() query) { - const token = query.token; + const { token } = query as { token: string }; if (!token) { throw new BadRequestException(); } @@ -276,7 +254,7 @@ export class AuthController { const { userId } = payload; await clearUserMetadata(userId); const response = await deleteUser(userId); - if (response.status === 'OK') { + if (response.status === "OK") { await SessionService.revokeAllSessionsForUser(userId); return {}; } else { @@ -291,7 +269,8 @@ export class AuthController { function isValidEmail(email: string) { const regexp = new RegExp( - /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + // eslint-disable-next-line no-useless-escape + /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ ); return regexp.test(email); } diff --git a/apps/api/src/auth/auth.middleware.ts b/apps/api/src/auth/auth.middleware.ts index a7d62ef..1cb0a43 100644 --- a/apps/api/src/auth/auth.middleware.ts +++ b/apps/api/src/auth/auth.middleware.ts @@ -1,15 +1,17 @@ -import { Injectable, NestMiddleware } from '@nestjs/common'; -import { middleware } from 'supertokens-node/framework/express'; +import type { NestMiddleware } from "@nestjs/common"; +import { Injectable } from "@nestjs/common"; +import type { Request, Response } from "express"; +import { middleware } from "supertokens-node/framework/express"; @Injectable() export class AuthMiddleware implements NestMiddleware { - supertokensMiddleware: any; + supertokensMiddleware: ReturnType; constructor() { this.supertokensMiddleware = middleware(); } - use(req: Request, res: any, next: () => void) { + use(req: Request, res: Response, next: () => void) { return this.supertokensMiddleware(req, res, next); } } diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts index ccf1fc9..f3c50d6 100644 --- a/apps/api/src/auth/auth.module.ts +++ b/apps/api/src/auth/auth.module.ts @@ -1,16 +1,13 @@ -import { - MiddlewareConsumer, - Module, - NestModule, - DynamicModule, -} from '@nestjs/common'; +import type { DynamicModule, MiddlewareConsumer, NestModule } from "@nestjs/common"; +import { Module } from "@nestjs/common"; +import { JwtModule } from "@nestjs/jwt"; -import { ConfigInjectionToken, AuthModuleConfig } from './config.interface'; -import { SupertokensService } from './supertokens/supertokens.service'; -import { AuthMiddleware } from './auth.middleware'; -import { AuthController } from './auth.controller'; -import { JwtModule } from '@nestjs/jwt'; -import { EmailService } from '../email/email.service'; +import { EmailService } from "../email/email.service"; +import { AuthController } from "./auth.controller"; +import { AuthMiddleware } from "./auth.middleware"; +import type { AuthModuleConfig} from "./config.interface"; +import { ConfigInjectionToken } from "./config.interface"; +import { SupertokensService } from "./supertokens/supertokens.service"; @Module({ imports: [JwtModule.register({})], @@ -20,14 +17,10 @@ import { EmailService } from '../email/email.service'; }) export class AuthModule implements NestModule { configure(consumer: MiddlewareConsumer) { - consumer.apply(AuthMiddleware).forRoutes('*'); + consumer.apply(AuthMiddleware).forRoutes("*"); } - static forRoot({ - connectionURI, - apiKey, - appInfo, - }: AuthModuleConfig): DynamicModule { + static forRoot({ connectionURI, apiKey, appInfo }: AuthModuleConfig): DynamicModule { return { providers: [ { diff --git a/apps/api/src/auth/config.interface.ts b/apps/api/src/auth/config.interface.ts index 7315eae..4665933 100644 --- a/apps/api/src/auth/config.interface.ts +++ b/apps/api/src/auth/config.interface.ts @@ -1,9 +1,9 @@ -import { AppInfo } from 'supertokens-node/types'; +import type { AppInfo } from "supertokens-node/types"; -export const ConfigInjectionToken = 'ConfigInjectionToken'; +export const ConfigInjectionToken = "ConfigInjectionToken"; -export type AuthModuleConfig = { +export interface AuthModuleConfig { appInfo: AppInfo; connectionURI: string; apiKey?: string; -}; +} diff --git a/apps/api/src/auth/filter/auth.filter.spec.ts b/apps/api/src/auth/filter/auth.filter.spec.ts index 7896d29..e18e06a 100644 --- a/apps/api/src/auth/filter/auth.filter.spec.ts +++ b/apps/api/src/auth/filter/auth.filter.spec.ts @@ -1,7 +1,7 @@ -import { SupertokensExceptionFilter } from './auth.filter'; +import { SupertokensExceptionFilter } from "./auth.filter"; -describe('AuthFilter', () => { - it('should be defined', () => { +describe("AuthFilter", () => { + it("should be defined", () => { expect(new SupertokensExceptionFilter()).toBeDefined(); }); }); diff --git a/apps/api/src/auth/filter/auth.filter.ts b/apps/api/src/auth/filter/auth.filter.ts index 27a3fdc..523efe8 100644 --- a/apps/api/src/auth/filter/auth.filter.ts +++ b/apps/api/src/auth/filter/auth.filter.ts @@ -1,14 +1,15 @@ -import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common'; -import { Request, Response, NextFunction, ErrorRequestHandler } from 'express'; - -import { errorHandler } from 'supertokens-node/framework/express'; -import { Error as STError } from 'supertokens-node'; +import type { ArgumentsHost, ExceptionFilter } from "@nestjs/common"; +import { Catch } from "@nestjs/common"; +import type { ErrorRequestHandler, NextFunction, Request, Response } from "express"; +import { Error as STError } from "supertokens-node"; +import { errorHandler } from "supertokens-node/framework/express"; @Catch(STError) export class SupertokensExceptionFilter implements ExceptionFilter { handler: ErrorRequestHandler; constructor() { + // eslint-disable-next-line @typescript-eslint/no-misused-promises this.handler = errorHandler(); } @@ -20,11 +21,6 @@ export class SupertokensExceptionFilter implements ExceptionFilter { return; } - this.handler( - exception, - ctx.getRequest(), - resp, - ctx.getNext(), - ); + this.handler(exception, ctx.getRequest(), resp, ctx.getNext()); } } diff --git a/apps/api/src/auth/filter/auth.guard.spec.ts b/apps/api/src/auth/filter/auth.guard.spec.ts index e4934ab..bc5f03c 100644 --- a/apps/api/src/auth/filter/auth.guard.spec.ts +++ b/apps/api/src/auth/filter/auth.guard.spec.ts @@ -1,7 +1,7 @@ -import { AuthGuard } from './auth.guard'; +import { AuthGuard } from "./auth.guard"; -describe('AuthGuard', () => { - it('should be defined', () => { +describe("AuthGuard", () => { + it("should be defined", () => { expect(new AuthGuard()).toBeDefined(); }); }); diff --git a/apps/api/src/auth/filter/auth.guard.ts b/apps/api/src/auth/filter/auth.guard.ts index 63c1440..4d9e9d6 100644 --- a/apps/api/src/auth/filter/auth.guard.ts +++ b/apps/api/src/auth/filter/auth.guard.ts @@ -1,8 +1,9 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import { Error as STError } from 'supertokens-node'; - -import { verifySession } from 'supertokens-node/recipe/session/framework/express'; -import { VerifySessionOptions } from 'supertokens-node/recipe/session'; +import type { CanActivate, ExecutionContext } from "@nestjs/common"; +import { Injectable } from "@nestjs/common"; +import type { Response } from "express"; +import { Error as STError } from "supertokens-node"; +import { VerifySessionOptions } from "supertokens-node/recipe/session"; +import { verifySession } from "supertokens-node/recipe/session/framework/express"; @Injectable() export class AuthGuard implements CanActivate { @@ -11,17 +12,17 @@ export class AuthGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const ctx = context.switchToHttp(); - let err = undefined; - const resp = ctx.getResponse(); + let err: Response | undefined = undefined; + const resp = ctx.getResponse(); // You can create an optional version of this by passing {sessionRequired: false} to verifySession await verifySession(this.verifyOptions)(ctx.getRequest(), resp, (res) => { - err = res; + err = res as Response; }); if (resp.headersSent) { throw new STError({ - message: 'RESPONSE_SENT', - type: 'RESPONSE_SENT', + message: "RESPONSE_SENT", + type: "RESPONSE_SENT", }); } diff --git a/apps/api/src/auth/session/session.decorator.ts b/apps/api/src/auth/session/session.decorator.ts deleted file mode 100644 index 2d77582..0000000 --- a/apps/api/src/auth/session/session.decorator.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createParamDecorator, ExecutionContext } from '@nestjs/common'; - -export const Session = createParamDecorator( - (data: unknown, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest(); - return request.session; - }, -); diff --git a/apps/api/src/auth/supertokens/supertokens.service.spec.ts b/apps/api/src/auth/supertokens/supertokens.service.spec.ts index cc164f0..d24a08a 100644 --- a/apps/api/src/auth/supertokens/supertokens.service.spec.ts +++ b/apps/api/src/auth/supertokens/supertokens.service.spec.ts @@ -1,7 +1,9 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SupertokensService } from './supertokens.service'; +import type { TestingModule } from "@nestjs/testing"; +import { Test } from "@nestjs/testing"; -describe('SupertokensService', () => { +import { SupertokensService } from "./supertokens.service"; + +describe("SupertokensService", () => { let service: SupertokensService; beforeEach(async () => { @@ -12,7 +14,7 @@ describe('SupertokensService', () => { service = module.get(SupertokensService); }); - it('should be defined', () => { + it("should be defined", () => { expect(service).toBeDefined(); }); }); diff --git a/apps/api/src/auth/supertokens/supertokens.service.ts b/apps/api/src/auth/supertokens/supertokens.service.ts index 34fb7cf..74a1ca4 100644 --- a/apps/api/src/auth/supertokens/supertokens.service.ts +++ b/apps/api/src/auth/supertokens/supertokens.service.ts @@ -1,19 +1,20 @@ -import { Inject, Injectable } from '@nestjs/common'; -import supertokens from 'supertokens-node'; -import Dashboard from 'supertokens-node/recipe/dashboard'; -import EmailVerification from 'supertokens-node/recipe/emailverification'; -import Session from 'supertokens-node/recipe/session'; -import ThirdPartyEmailPassword from 'supertokens-node/recipe/thirdpartyemailpassword'; -import UserMetadata from 'supertokens-node/recipe/usermetadata'; -import { AuthModuleConfig, ConfigInjectionToken } from '../config.interface'; -import { EmailService } from '../../email/email.service'; -import UserRoles from 'supertokens-node/recipe/userroles'; +import { Inject, Injectable } from "@nestjs/common"; +import supertokens from "supertokens-node"; +import Dashboard from "supertokens-node/recipe/dashboard"; +import EmailVerification from "supertokens-node/recipe/emailverification"; +import Session from "supertokens-node/recipe/session"; +import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword"; +import UserMetadata from "supertokens-node/recipe/usermetadata"; +import UserRoles from "supertokens-node/recipe/userroles"; + +import { EmailService } from "../../email/email.service"; +import { AuthModuleConfig, ConfigInjectionToken } from "../config.interface"; @Injectable() export class SupertokensService { constructor( @Inject(ConfigInjectionToken) private config: AuthModuleConfig, - private emailService: EmailService, + private emailService: EmailService ) { supertokens.init({ appInfo: config.appInfo, @@ -26,10 +27,10 @@ export class SupertokensService { signUpFeature: { formFields: [ { - id: 'first_name', + id: "first_name", }, { - id: 'last_name', + id: "last_name", }, ], }, @@ -51,17 +52,11 @@ export class SupertokensService { return { ...originalImplementation, thirdPartySignInUpPOST: async function (input) { - if ( - originalImplementation.thirdPartySignInUpPOST === undefined - ) { - throw Error('Should never come here'); + if (originalImplementation.thirdPartySignInUpPOST === undefined) { + throw Error("Should never come here"); } - const response = - await originalImplementation.thirdPartySignInUpPOST(input); - if ( - response.status === 'OK' && - response.createdNewRecipeUser - ) { + const response = await originalImplementation.thirdPartySignInUpPOST(input); + if (response.status === "OK" && response.createdNewRecipeUser) { let updatedUser: | { first_name?: string; @@ -70,97 +65,75 @@ export class SupertokensService { } | undefined = undefined; const [thirdParty] = response.user.thirdParty; - if (thirdParty.id === 'apple') { - const user = response.rawUserInfoFromProvider - .fromIdTokenPayload.user as { - email: string; - first_name: string; - last_name: string; - }; - updatedUser = { - first_name: user.first_name, - last_name: user.last_name, - }; - } else if (thirdParty.id === 'github') { - const rawUser = response.rawUserInfoFromProvider - .fromUserInfoAPI as { - user: { - name: string; - avatar_url?: string; + if (thirdParty) { + if (thirdParty.id === "apple") { + const user = response.rawUserInfoFromProvider.fromIdTokenPayload?.user as { + email: string; + first_name: string; + last_name: string; + }; + updatedUser = { + first_name: user.first_name, + last_name: user.last_name, + }; + } else if (thirdParty.id === "github") { + const rawUser = response.rawUserInfoFromProvider.fromUserInfoAPI as { + user: { + name: string; + avatar_url?: string; + }; + }; + updatedUser = { + first_name: rawUser.user.name, + avatarUrl: rawUser.user?.avatar_url ?? undefined, + last_name: "", }; - }; - updatedUser = { - first_name: rawUser.user.name, - avatarUrl: typeof rawUser.user.avatar_url - ? rawUser.user?.avatar_url - : undefined, - last_name: '', - }; - } else if (thirdParty.id === 'google') { - const user = response.rawUserInfoFromProvider - .fromIdTokenPayload as { - email: string; - given_name: string; - family_name: string; - picture: string; - }; - updatedUser = { - first_name: user.given_name, - last_name: user.family_name, - avatarUrl: user.picture, - }; + } else if (thirdParty.id === "google") { + const user = response.rawUserInfoFromProvider.fromIdTokenPayload as { + email: string; + given_name: string; + family_name: string; + picture: string; + }; + updatedUser = { + first_name: user.given_name, + last_name: user.family_name, + avatarUrl: user.picture, + }; + } } - await Promise.all([ - UserMetadata.updateUserMetadata( - response.user.id, - updatedUser, - ), - UserRoles.createNewRoleOrAddPermissions('user', [ - 'read', - 'write', - ]), - ]); - await UserRoles.addRoleToUser( - input.tenantId, - response.user.id, - 'user', - ); + const updatePromises: Promise[] = [ + UserRoles.createNewRoleOrAddPermissions("user", ["read", "write"]), + ]; + if (updatedUser) { + updatePromises.push(UserMetadata.updateUserMetadata(response.user.id, updatedUser)); + } + await Promise.all(updatePromises); + await UserRoles.addRoleToUser(input.tenantId, response.user.id, "user"); } return response; }, emailPasswordSignUpPOST: async function (input) { - if ( - originalImplementation.emailPasswordSignUpPOST === undefined - ) { - throw Error('Should never come here'); + if (originalImplementation.emailPasswordSignUpPOST === undefined) { + throw Error("Should never come here"); } - const response = - await originalImplementation.emailPasswordSignUpPOST(input); - if (response.status === 'OK') { + const response = await originalImplementation.emailPasswordSignUpPOST(input); + if (response.status === "OK") { const formFields = input.formFields; await UserMetadata.updateUserMetadata(response.user.id, { - first_name: formFields.find((f) => f.id === 'first_name') - .value, - last_name: formFields.find((f) => f.id === 'last_name') - .value, + first_name: formFields.find((f) => f.id === "first_name")?.value, + last_name: formFields.find((f) => f.id === "last_name")?.value, }); await EmailVerification.sendEmailVerificationEmail( input.tenantId, response.user.id, response.session.getRecipeUserId(), response.user.emails[0], - input.userContext, - ); - await UserRoles.createNewRoleOrAddPermissions('user', [ - 'read', - 'write', - ]); - await UserRoles.addRoleToUser( - input.tenantId, - response.user.id, - 'user', + input.userContext ); + await UserRoles.createNewRoleOrAddPermissions("user", ["read", "write"]); + await UserRoles.addRoleToUser(input.tenantId, response.user.id, "user"); } return response; @@ -171,11 +144,11 @@ export class SupertokensService { providers: [ { config: { - thirdPartyId: 'apple', + thirdPartyId: "apple", clients: [ { - clientId: process.env.APPLE_CLIENT_ID, - clientType: 'service', + clientId: process.env.APPLE_CLIENT_ID ?? "", + clientType: "service", additionalConfig: { keyId: process.env.APPLE_KEY_ID, privateKey: process.env.APPLE_PRIVATE_KEY, @@ -187,11 +160,11 @@ export class SupertokensService { }, { config: { - thirdPartyId: 'github', + thirdPartyId: "github", clients: [ { - clientType: 'ios', - clientId: process.env.GITHUB_CLIENT_ID, + clientType: "ios", + clientId: process.env.GITHUB_CLIENT_ID ?? "", clientSecret: process.env.GITHUB_CLIENT_SECRET, }, ], @@ -199,12 +172,12 @@ export class SupertokensService { }, { config: { - thirdPartyId: 'google', + thirdPartyId: "google", clients: [ { - clientId: process.env.GOOGLE_CLIENT_ID, + clientId: process.env.GOOGLE_CLIENT_ID ?? "", clientSecret: process.env.GOOGLE_CLIENT_SECRET, - clientType: 'ios', + clientType: "ios", }, ], }, @@ -213,7 +186,7 @@ export class SupertokensService { }), UserRoles.init(), EmailVerification.init({ - mode: 'REQUIRED', + mode: "REQUIRED", emailDelivery: { override: (originalImplementation) => { return { diff --git a/apps/api/src/email/email.service.ts b/apps/api/src/email/email.service.ts index 286d9b6..485943d 100644 --- a/apps/api/src/email/email.service.ts +++ b/apps/api/src/email/email.service.ts @@ -1,23 +1,18 @@ -import { Resend } from 'resend'; -import { - CreateEmailOptions, - CreateEmailResponse, -} from 'resend/build/src/emails/interfaces'; -import Handlebars from 'handlebars'; -import { readFileSync } from 'fs'; -import { join } from 'path'; -import { Injectable } from '@nestjs/common'; +import { readFile } from "fs/promises"; +import { join } from "path"; +import { Injectable } from "@nestjs/common"; +import Handlebars from "handlebars"; +import { Resend } from "resend"; +import type { CreateEmailOptions, CreateEmailResponse } from "resend/build/src/emails/interfaces"; @Injectable() export class EmailService extends Resend { constructor() { super(process.env.RESEND_API_KEY); } - async send( - data: Omit & { from?: string }, - ): Promise { + async send(data: Omit & { from?: string }): Promise { return this.emails.send({ - from: process.env.EMAIL_FROM || '', + from: process.env.EMAIL_FROM ?? "", ...data, } as CreateEmailOptions); } @@ -29,17 +24,14 @@ export class EmailService extends Resend { email: string; passwordResetLink: string; }): Promise { - const template = await readFileSync( - join(__dirname, '..', '..', 'emails', 'auth', 'reset-password.hbs'), - 'utf8', - ); + const template = await readFile(join(__dirname, "..", "..", "emails", "auth", "reset-password.hbs"), "utf8"); const content = Handlebars.compile(template)({ passwordResetLink, email, }); return await this.send({ to: email, - subject: 'Reset Password', + subject: "Reset Password", html: content, }); } @@ -51,17 +43,14 @@ export class EmailService extends Resend { email: string; emailVerifyLink: string; }): Promise { - const template = await readFileSync( - join(__dirname, '..', '..', 'emails', 'auth', 'email-verification.hbs'), - 'utf8', - ); + const template = await readFile(join(__dirname, "..", "..", "emails", "auth", "email-verification.hbs"), "utf8"); const content = Handlebars.compile(template)({ verificationLink: emailVerifyLink, email, }); return await this.send({ to: email, - subject: 'Email Verification', + subject: "Email Verification", html: content, }); } @@ -73,17 +62,14 @@ export class EmailService extends Resend { email: string; accountDeletionLink: string; }): Promise { - const template = await readFileSync( - join(__dirname, '..', '..', 'emails', 'auth', 'delete-account.hbs'), - 'utf8', - ); + const template = await readFile(join(__dirname, "..", "..", "emails", "auth", "delete-account.hbs"), "utf8"); const content = Handlebars.compile(template)({ accountDeletionLink, email, }); return await this.send({ to: email, - subject: 'Delete Account Request', + subject: "Delete Account Request", html: content, }); } diff --git a/apps/api/src/logger/logger.middleware.ts b/apps/api/src/logger/logger.middleware.ts index dd331e8..5660a4f 100644 --- a/apps/api/src/logger/logger.middleware.ts +++ b/apps/api/src/logger/logger.middleware.ts @@ -1,19 +1,20 @@ -import { Injectable, NestMiddleware, Logger } from '@nestjs/common'; - -import { Request, Response, NextFunction } from 'express'; +import type { NestMiddleware } from "@nestjs/common"; +import { Injectable, Logger } from "@nestjs/common"; +import type { NextFunction, Request, Response } from "express"; @Injectable() export class LoggerMiddleware implements NestMiddleware { - private logger = new Logger('HTTP'); + private logger = new Logger("HTTP"); use(request: Request, response: Response, next: NextFunction): void { const { method } = request; - const ip = request.get('X-Real-IP') || request.ip; - const userAgent = request.get('user-agent') || ''; - response.on('close', () => { + const ip = request.get("X-Real-IP") ?? request.ip; + const userAgent = request.get("user-agent") ?? ""; + response.on("close", () => { const { statusCode } = response; - const contentLength = response.get('content-length'); + const contentLength = response.get("content-length"); const messsage = `${method} ${ + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access request.baseUrl || request.route?.path || request.path } ${statusCode} ${contentLength} - ${userAgent} ${ip}`; if (statusCode >= 200 && statusCode < 400) { diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index bceb75d..d8fa307 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -1,31 +1,32 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; -import supertokens from 'supertokens-node'; -import { SupertokensExceptionFilter } from './auth/filter/auth.filter'; -import { NestExpressApplication } from '@nestjs/platform-express'; -import { join } from 'path'; -import { json, urlencoded } from 'express'; +import { join } from "path"; +import { NestFactory } from "@nestjs/core"; +import type { NestExpressApplication } from "@nestjs/platform-express"; +import { json, urlencoded } from "express"; +import supertokens from "supertokens-node"; + +import { AppModule } from "./app.module"; +import { SupertokensExceptionFilter } from "./auth/filter/auth.filter"; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors({ - origin: ['http://localhost:3000'], - allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()], + origin: ["http://localhost:3000"], + allowedHeaders: ["content-type", ...supertokens.getAllCORSHeaders()], credentials: true, }); - app.useStaticAssets(join(__dirname, '..', 'public'), { - setHeaders: (res, path) => { - if (!path.endsWith('json')) { - res.set('Content-Type', 'application/json'); + app.useStaticAssets(join(__dirname, "..", "public"), { + setHeaders: (res: NestExpressApplication, path) => { + if (!path.endsWith("json")) { + res.set("Content-Type", "application/json"); } }, }); - app.setBaseViewsDir(join(__dirname, '..', 'views')); - app.setViewEngine('hbs'); + app.setBaseViewsDir(join(__dirname, "..", "views")); + app.setViewEngine("hbs"); app.useGlobalFilters(new SupertokensExceptionFilter()); - app.use(json({ limit: '5mb' })); - app.use(urlencoded({ extended: true, limit: '5mb' })); - await app.listen(process.env.PORT || 3000); + app.use(json({ limit: "5mb" })); + app.use(urlencoded({ extended: true, limit: "5mb" })); + await app.listen(process.env.PORT ?? 3000); } -bootstrap(); +void bootstrap(); diff --git a/apps/api/src/user/user.controller.spec.ts b/apps/api/src/user/user.controller.spec.ts index 1f38440..276b93b 100644 --- a/apps/api/src/user/user.controller.spec.ts +++ b/apps/api/src/user/user.controller.spec.ts @@ -1,8 +1,10 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserController } from './user.controller'; -import { UserService } from './user.service'; +import type { TestingModule } from "@nestjs/testing"; +import { Test } from "@nestjs/testing"; -describe('UserController', () => { +import { UserController } from "./user.controller"; +import { UserService } from "./user.service"; + +describe("UserController", () => { let controller: UserController; beforeEach(async () => { @@ -14,7 +16,7 @@ describe('UserController', () => { controller = module.get(UserController); }); - it('should be defined', () => { + it("should be defined", () => { expect(controller).toBeDefined(); }); }); diff --git a/apps/api/src/user/user.controller.ts b/apps/api/src/user/user.controller.ts index 01a861b..8753263 100644 --- a/apps/api/src/user/user.controller.ts +++ b/apps/api/src/user/user.controller.ts @@ -1,8 +1,9 @@ -import { Controller, UseGuards } from '@nestjs/common'; -import { AuthGuard } from '../auth/filter/auth.guard'; -import { UserService } from './user.service'; +import { Controller, UseGuards } from "@nestjs/common"; -@Controller('user') +import { AuthGuard } from "../auth/filter/auth.guard"; +import { UserService } from "./user.service"; + +@Controller("user") @UseGuards(new AuthGuard()) export class UserController { constructor(private readonly userService: UserService) {} diff --git a/apps/api/src/user/user.module.ts b/apps/api/src/user/user.module.ts index e21d51f..1ede9b7 100644 --- a/apps/api/src/user/user.module.ts +++ b/apps/api/src/user/user.module.ts @@ -1,6 +1,7 @@ -import { Module } from '@nestjs/common'; -import { UserService } from './user.service'; -import { UserController } from './user.controller'; +import { Module } from "@nestjs/common"; + +import { UserController } from "./user.controller"; +import { UserService } from "./user.service"; @Module({ controllers: [UserController], diff --git a/apps/api/src/user/user.service.spec.ts b/apps/api/src/user/user.service.spec.ts index 873de8a..204a993 100644 --- a/apps/api/src/user/user.service.spec.ts +++ b/apps/api/src/user/user.service.spec.ts @@ -1,7 +1,9 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserService } from './user.service'; +import type { TestingModule } from "@nestjs/testing"; +import { Test } from "@nestjs/testing"; -describe('UserService', () => { +import { UserService } from "./user.service"; + +describe("UserService", () => { let service: UserService; beforeEach(async () => { @@ -12,7 +14,7 @@ describe('UserService', () => { service = module.get(UserService); }); - it('should be defined', () => { + it("should be defined", () => { expect(service).toBeDefined(); }); }); diff --git a/apps/api/src/user/user.service.ts b/apps/api/src/user/user.service.ts index 668a7d6..724844d 100644 --- a/apps/api/src/user/user.service.ts +++ b/apps/api/src/user/user.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable } from "@nestjs/common"; @Injectable() export class UserService {} diff --git a/apps/api/test/app.e2e-spec.ts b/apps/api/test/app.e2e-spec.ts index 50cda62..7b4d83c 100644 --- a/apps/api/test/app.e2e-spec.ts +++ b/apps/api/test/app.e2e-spec.ts @@ -1,9 +1,11 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; +import type { INestApplication } from "@nestjs/common"; +import type { TestingModule } from "@nestjs/testing"; +import { Test } from "@nestjs/testing"; +import request from "supertest"; -describe('AppController (e2e)', () => { +import { AppModule } from "./../src/app.module"; + +describe("AppController (e2e)", () => { let app: INestApplication; beforeEach(async () => { @@ -15,10 +17,7 @@ describe('AppController (e2e)', () => { await app.init(); }); - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); + it("/ (GET)", () => { + return request(app.getHttpServer()).get("/").expect(200).expect("Hello World!"); }); }); diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 95f5641..3062a82 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": ["@acme/tsconfig/base"], "compilerOptions": { "module": "commonjs", "declaration": true, @@ -8,11 +9,12 @@ "allowSyntheticDefaultImports": true, "target": "ES2021", "sourceMap": true, + "noEmit": false, "outDir": "./dist", "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, + "strictNullChecks": true, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, diff --git a/apps/expo/app.config.ts b/apps/expo/app.config.ts index 7e59938..3b640bb 100644 --- a/apps/expo/app.config.ts +++ b/apps/expo/app.config.ts @@ -1,26 +1,26 @@ -import { ExpoConfig, ConfigContext } from 'expo/config' +import type { ConfigContext, ExpoConfig } from "expo/config"; export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, - name: 'expo-supertokens', - slug: 'expo-supertokens', - version: '1.0.0', - orientation: 'portrait', - icon: './assets/images/icon.png', - scheme: 'myapp', - userInterfaceStyle: 'automatic', + name: "expo-supertokens", + slug: "expo-supertokens", + version: "1.0.0", + orientation: "portrait", + icon: "./assets/images/icon.png", + scheme: "myapp", + userInterfaceStyle: "automatic", splash: { - image: './assets/images/splash.png', - resizeMode: 'contain', - backgroundColor: '#ffffff', + image: "./assets/images/splash.png", + resizeMode: "contain", + backgroundColor: "#ffffff", }, - assetBundlePatterns: ['**/*'], + assetBundlePatterns: ["**/*"], ios: { supportsTablet: true, usesAppleSignIn: true, - bundleIdentifier: 'com.lukesthl.expo-supertokens', + bundleIdentifier: "com.lukesthl.expo-supertokens", googleServicesFile: process.env.GOOGLE_SERVICES_FILE, - associatedDomains: [process.env.ASSOCIATED_DOMAIN || ''], + associatedDomains: [process.env.ASSOCIATED_DOMAIN ?? ""], infoPlist: { CFBundleURLTypes: [ { @@ -31,25 +31,25 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ }, android: { adaptiveIcon: { - foregroundImage: './assets/images/adaptive-icon.png', - backgroundColor: '#ffffff', + foregroundImage: "./assets/images/adaptive-icon.png", + backgroundColor: "#ffffff", }, - permissions: ['android.permission.RECORD_AUDIO'], - googleServicesFile: './google-services.json', + permissions: ["android.permission.RECORD_AUDIO"], + googleServicesFile: "./google-services.json", }, web: { - bundler: 'metro', + bundler: "metro", // output: 'static', - favicon: './assets/images/favicon.png', + favicon: "./assets/images/favicon.png", }, plugins: [ - 'expo-router', - 'expo-apple-authentication', - '@react-native-google-signin/google-signin', + "expo-router", + "expo-apple-authentication", + "@react-native-google-signin/google-signin", [ - 'expo-image-picker', + "expo-image-picker", { - photosPermission: 'The app accesses your photos to let you share them with your friends.', + photosPermission: "The app accesses your photos to let you share them with your friends.", }, ], ], @@ -61,7 +61,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ origin: false, }, eas: { - projectId: 'fb88c696-3171-471e-a8e2-fb4ac671b225', + projectId: "fb88c696-3171-471e-a8e2-fb4ac671b225", }, }, -}) +}); diff --git a/apps/expo/app/(app)/(authorized)/_layout.tsx b/apps/expo/app/(app)/(authorized)/_layout.tsx index d7c64ec..4b0f13d 100644 --- a/apps/expo/app/(app)/(authorized)/_layout.tsx +++ b/apps/expo/app/(app)/(authorized)/_layout.tsx @@ -1,13 +1,14 @@ -import { Home, User } from "@tamagui/lucide-icons"; -import { Toast, useToastState } from "@tamagui/toast"; +import { useColorScheme } from "react-native"; import { BlurView } from "expo-blur"; import { Tabs } from "expo-router"; +import { Home, User } from "@tamagui/lucide-icons"; +import { Toast, useToastState } from "@tamagui/toast"; import { observer } from "mobx-react-lite"; import { View, YStack } from "tamagui"; -import { translate } from "../../../components/translate"; -import { useColorScheme } from "react-native"; -import { AuthStore } from "../../../components/auth/auth.store"; + import { AuthProvider } from "../../../components/auth/auth.provider"; +import { AuthStore } from "../../../components/auth/auth.store"; +import { translate } from "../../../components/translate"; const AuthorisedLayout = observer(() => { return ( @@ -63,24 +64,11 @@ const DefaultToast = () => { animation={"quick"} bg="$backgroundTransparent" > - - + + {currentToast.title} - {!!currentToast.message && ( - - {currentToast.message} - - )} + {!!currentToast.message && {currentToast.message}} diff --git a/apps/expo/app/(app)/(authorized)/account/_layout.tsx b/apps/expo/app/(app)/(authorized)/account/_layout.tsx index b6cebc7..d460046 100644 --- a/apps/expo/app/(app)/(authorized)/account/_layout.tsx +++ b/apps/expo/app/(app)/(authorized)/account/_layout.tsx @@ -1,5 +1,6 @@ import { Stack } from "expo-router"; import { observer } from "mobx-react-lite"; + import { translate } from "../../../../components/translate"; export const unstable_settings = { diff --git a/apps/expo/app/(app)/(authorized)/account/delete-account/modal.tsx b/apps/expo/app/(app)/(authorized)/account/delete-account/modal.tsx index a04759a..bee7a3a 100644 --- a/apps/expo/app/(app)/(authorized)/account/delete-account/modal.tsx +++ b/apps/expo/app/(app)/(authorized)/account/delete-account/modal.tsx @@ -1,16 +1,15 @@ +import { router } from "expo-router"; +import { StatusBar } from "expo-status-bar"; import { LmButton } from "@tamagui-extras/core"; import { LmInput } from "@tamagui-extras/form"; -import { StatusBar } from "expo-status-bar"; -import { values } from "mobx"; -import { Heading, Text, View, YStack } from "tamagui"; -import { translate } from "../../../../../components/translate"; -import { DismissKeyboard } from "../../../../../components/keyboard"; -import { AuthError } from "expo-auth-session"; -import { router } from "expo-router"; +import { AlertTriangle } from "@tamagui/lucide-icons"; +import { useToastController } from "@tamagui/toast"; import { Formik } from "formik"; +import { Heading, Text, View, YStack } from "tamagui"; + import { AuthStore } from "../../../../../components/auth/auth.store"; -import { useToastController } from "@tamagui/toast"; -import { AlertTriangle } from "@tamagui/lucide-icons"; +import { DismissKeyboard } from "../../../../../components/keyboard"; +import { translate } from "../../../../../components/translate"; export default function DeleteAccountModal() { const toast = useToastController(); @@ -24,13 +23,9 @@ export default function DeleteAccountModal() { validate={(values) => { const errors: Record = {}; if (!values.email) { - errors.email = translate.t( - "account.deleteaccount.errors.email.required" - ); + errors.email = translate.t("account.deleteaccount.errors.email.required"); } else if (values.email !== AuthStore.user.email) { - errors.email = translate.t( - "account.deleteaccount.errors.email.notmatch" - ); + errors.email = translate.t("account.deleteaccount.errors.email.notmatch"); } return errors; }} @@ -50,21 +45,14 @@ export default function DeleteAccountModal() { setSubmitting(false); }} > - {({ - values, - errors, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - }) => ( - + {({ values, errors, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( + {translate.t("account.deleteaccount.title")} - + {translate.t("account.deleteaccount.description")} - + E-Mail: {AuthStore.user.email} @@ -85,11 +73,11 @@ export default function DeleteAccountModal() { onPress={() => handleSubmit()} loading={isSubmitting} icon={} - bg="$red10" + backgroundColor="$red10" > {translate.t("account.deleteaccount.submit")} - router.back()} mt="$2"> + router.back()} marginTop="$2"> {translate.t("account.deleteaccount.cancel")} diff --git a/apps/expo/app/(app)/(authorized)/account/email/change.tsx b/apps/expo/app/(app)/(authorized)/account/email/change.tsx index c02c091..c85191f 100644 --- a/apps/expo/app/(app)/(authorized)/account/email/change.tsx +++ b/apps/expo/app/(app)/(authorized)/account/email/change.tsx @@ -1,18 +1,16 @@ +import React from "react"; +import { router, Stack } from "expo-router"; import { LmButton } from "@tamagui-extras/core"; import { LmInput } from "@tamagui-extras/form"; import { useToastController } from "@tamagui/toast"; -import { Stack, router } from "expo-router"; import { Formik } from "formik"; import { observer } from "mobx-react-lite"; -import React from "react"; import { Heading, Text, View, YStack } from "tamagui"; + import { AuthError } from "../../../../../components/auth/auth.error"; import { AuthStore } from "../../../../../components/auth/auth.store"; +import { DismissKeyboard, useSoftKeyboardEffect } from "../../../../../components/keyboard"; import { translate } from "../../../../../components/translate"; -import { - DismissKeyboard, - useSoftKeyboardEffect, -} from "../../../../../components/keyboard"; const ChangeEmail = observer(() => { const toast = useToastController(); @@ -32,9 +30,7 @@ const ChangeEmail = observer(() => { validate={(values) => { const errors: Record = {}; if (!values.email) { - errors.email = translate.t( - "account.changeemail.errors.email.required" - ); + errors.email = translate.t("account.changeemail.errors.email.required"); } return errors; }} @@ -53,18 +49,15 @@ const ChangeEmail = observer(() => { const errors = error.formFields.map((field) => ({ [field.id]: field.error, })); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument setErrors(Object.assign({}, ...errors)); } else if (error.name === "EMAIL_ALREADY_EXISTS_ERROR") { setErrors({ - email: translate.t( - "account.changeemail.errors.email.alreadyexists" - ), + email: translate.t("account.changeemail.errors.email.alreadyexists"), }); } else { setErrors({ - email: translate.t( - "account.changeemail.errors.unkownError" - ), + email: translate.t("account.changeemail.errors.unkownError"), }); } } else { @@ -77,15 +70,8 @@ const ChangeEmail = observer(() => { setSubmitting(false); }} > - {({ - values, - errors, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - }) => ( - + {({ values, errors, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( + {translate.t("account.changeemail.subHeadline")} {translate.t("account.changeemail.title")} diff --git a/apps/expo/app/(app)/(authorized)/account/index.tsx b/apps/expo/app/(app)/(authorized)/account/index.tsx index 4104a8b..e5c1469 100644 --- a/apps/expo/app/(app)/(authorized)/account/index.tsx +++ b/apps/expo/app/(app)/(authorized)/account/index.tsx @@ -1,3 +1,7 @@ +import { useState } from "react"; +import { Linking, Platform, RefreshControl, useColorScheme } from "react-native"; +import type { Href } from "expo-router"; +import { Link, router, Stack } from "expo-router"; import { AlertTriangle, Building2, @@ -12,27 +16,9 @@ import { User2, } from "@tamagui/lucide-icons"; import { useToastController } from "@tamagui/toast"; -import { Href, Link, Stack, router } from "expo-router"; import { observer } from "mobx-react-lite"; -import { useState } from "react"; -import { - Linking, - Platform, - RefreshControl, - useColorScheme, -} from "react-native"; -import { - Avatar, - H4, - Heading, - ListItem, - ScrollView, - Separator, - View, - YGroup, - YStack, - getTokens, -} from "tamagui"; +import { Avatar, getTokens, H4, Heading, ListItem, ScrollView, Separator, View, YGroup, YStack } from "tamagui"; + import { AuthStore } from "../../../../components/auth/auth.store"; import { translate } from "../../../../components/translate"; import { appConfig } from "../../../../constants/app.config"; @@ -44,29 +30,19 @@ const Account = observer(() => { const onRefresh = () => { setRefreshing(true); - AuthStore.loadUser().then(() => { + void AuthStore.loadUser().then(() => { setRefreshing(false); }); }; return ( <> - - } - > - + }> + - {AuthStore.user.avatarUrl && ( - - )} - + {AuthStore.user.avatarUrl && } + @@ -74,12 +50,7 @@ const Account = observer(() => { {AuthStore.user.firstName} {AuthStore.user.lastName} - } - > + }> { title={translate.t("account.general.user.title")} subTitle={translate.t("account.general.user.description")} icon={ - + } @@ -99,17 +70,12 @@ const Account = observer(() => { 0 ? 0.65 : 1} onPress={() => { - if (AuthStore.user.thirdParty) { - toast.show( - translate.t("account.general.email.thirdpartyinfo.title"), - { - message: translate.t( - "account.general.email.thirdpartyinfo.description" - ), - } - ); + if (AuthStore.user.thirdParty.length > 0) { + toast.show(translate.t("account.general.email.thirdpartyinfo.title"), { + message: translate.t("account.general.email.thirdpartyinfo.description"), + }); } else { router.push("/account/email/change"); } @@ -117,7 +83,7 @@ const Account = observer(() => { title={translate.t("account.general.email.title")} subTitle={translate.t("account.general.email.description")} icon={ - + } @@ -128,19 +94,12 @@ const Account = observer(() => { 0 ? 0.65 : 1} onPress={() => { - if (AuthStore.user.thirdParty) { - toast.show( - translate.t( - "account.general.password.thirdpartyinfo.title" - ), - { - message: translate.t( - "account.general.password.thirdpartyinfo.description" - ), - } - ); + if (AuthStore.user.thirdParty.length > 0) { + toast.show(translate.t("account.general.password.thirdpartyinfo.title"), { + message: translate.t("account.general.password.thirdpartyinfo.description"), + }); } else { router.push("/account/password/change"); } @@ -148,7 +107,7 @@ const Account = observer(() => { title={translate.t("account.general.password.title")} subTitle={translate.t("account.general.password.description")} icon={ - + } @@ -169,7 +128,7 @@ const Account = observer(() => { title={translate.t("account.general.settings.title")} subTitle={translate.t("account.general.settings.description")} icon={ - + } @@ -178,27 +137,20 @@ const Account = observer(() => {

{translate.t("account.legal.title")}

- } - > + }> } asChild> + } iconAfter={ChevronRight} > - - {translate.t("account.legal.privacypolicy.title")} - + {translate.t("account.legal.privacypolicy.title")} @@ -207,15 +159,13 @@ const Account = observer(() => { hoverTheme pressTheme icon={ - + } iconAfter={ChevronRight} > - - {translate.t("account.legal.termsandconditions.title")} - + {translate.t("account.legal.termsandconditions.title")} @@ -224,36 +174,29 @@ const Account = observer(() => { hoverTheme pressTheme icon={ - + } iconAfter={ChevronRight} > - - {translate.t("account.legal.imprint.title")} - + {translate.t("account.legal.imprint.title")} - } - > + }> - + + } onPress={() => { - AuthStore.signOut(); + void AuthStore.signOut(); }} iconAfter={ChevronRight} > @@ -261,24 +204,22 @@ const Account = observer(() => { - +
} iconAfter={ChevronRight} > - - {translate.t("account.deleteaccount.title")} - + {translate.t("account.deleteaccount.title")} diff --git a/apps/expo/app/(app)/(authorized)/account/password/change.tsx b/apps/expo/app/(app)/(authorized)/account/password/change.tsx index c45aa0d..4eecad6 100644 --- a/apps/expo/app/(app)/(authorized)/account/password/change.tsx +++ b/apps/expo/app/(app)/(authorized)/account/password/change.tsx @@ -1,17 +1,15 @@ +import React from "react"; +import { router, Stack } from "expo-router"; import { LmButton } from "@tamagui-extras/core"; import { LmInput } from "@tamagui-extras/form"; import { useToastController } from "@tamagui/toast"; -import { Stack, router } from "expo-router"; import { Formik } from "formik"; import { observer } from "mobx-react-lite"; -import React from "react"; import { Heading, Text, View, YStack } from "tamagui"; + import { AuthError } from "../../../../../components/auth/auth.error"; import { AuthStore } from "../../../../../components/auth/auth.store"; -import { - DismissKeyboard, - useSoftKeyboardEffect, -} from "../../../../../components/keyboard"; +import { DismissKeyboard, useSoftKeyboardEffect } from "../../../../../components/keyboard"; import { translate } from "../../../../../components/translate"; const ChangePassword = observer(() => { @@ -32,18 +30,12 @@ const ChangePassword = observer(() => { validate={(values) => { const errors: Record = {}; if (!values.oldPassword) { - errors.oldPassword = translate.t( - "account.changepassword.errors.oldpassword.required" - ); + errors.oldPassword = translate.t("account.changepassword.errors.oldpassword.required"); } else if (!values.password) { - errors.password = translate.t( - "account.changepassword.errors.password.required" - ); + errors.password = translate.t("account.changepassword.errors.password.required"); } if (values.password !== values.passwordConfirm) { - errors.password = translate.t( - "account.changepassword.errors.password.notequals" - ); + errors.password = translate.t("account.changepassword.errors.password.notequals"); } return errors; }} @@ -61,28 +53,20 @@ const ChangePassword = observer(() => { if (error instanceof AuthError) { if (error?.name === "WRONG_CREDENTIALS_ERROR") { setErrors({ - oldPassword: translate.t( - "account.changepassword.errors.oldpassword.invalid" - ), + oldPassword: translate.t("account.changepassword.errors.oldpassword.invalid"), }); } else if (error.name === "PASSWORD_POLICY_VIOLATED_ERROR") { setErrors({ - password: translate.t( - "account.changepassword.errors.password.passwordStrength" - ), + password: translate.t("account.changepassword.errors.password.passwordStrength"), }); } else { setErrors({ - password: translate.t( - "account.changepassword.errors.unkownError" - ), + password: translate.t("account.changepassword.errors.unkownError"), }); } } else { setErrors({ - password: translate.t( - "account.changepassword.errors.unkownError" - ), + password: translate.t("account.changepassword.errors.unkownError"), }); console.log(error); } @@ -90,26 +74,17 @@ const ChangePassword = observer(() => { setSubmitting(false); }} > - {({ - values, - errors, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - }) => ( - + {({ values, errors, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( + {translate.t("account.changepassword.subHeadline")} {translate.t("account.changepassword.title")} - + { onSubmitEditing={() => handleSubmit()} value={values.passwordConfirm} error={!!errors.passwordConfirm} - placeholder={translate.t( - "account.changepassword.passwordConfirm" - )} + placeholder={translate.t("account.changepassword.passwordConfirm")} passwordIconProps={{ color: "$gray10" }} /> - {(errors.oldPassword || - errors.password || - errors.passwordConfirm) && ( - - {errors.passwordConfirm || - errors.password || - errors.oldPassword} - + {(errors.oldPassword ?? errors.password ?? errors.passwordConfirm) && ( + {errors.passwordConfirm ?? errors.password ?? errors.oldPassword} )} handleSubmit()} loading={isSubmitting}> diff --git a/apps/expo/app/(app)/(authorized)/account/user/edit.tsx b/apps/expo/app/(app)/(authorized)/account/user/edit.tsx index d20778b..5a26a33 100644 --- a/apps/expo/app/(app)/(authorized)/account/user/edit.tsx +++ b/apps/expo/app/(app)/(authorized)/account/user/edit.tsx @@ -1,18 +1,16 @@ +import React from "react"; +import * as ImagePicker from "expo-image-picker"; +import { router, Stack } from "expo-router"; import { LmButton } from "@tamagui-extras/core"; import { Upload, X } from "@tamagui/lucide-icons"; import { useToastController } from "@tamagui/toast"; -import * as ImagePicker from "expo-image-picker"; -import { Stack, router } from "expo-router"; import { Formik } from "formik"; import { observer } from "mobx-react-lite"; -import React from "react"; import { Heading, Image, Input, Text, View, XStack, YStack } from "tamagui"; + import { AuthStore } from "../../../../../components/auth/auth.store"; +import { DismissKeyboard, useSoftKeyboardEffect } from "../../../../../components/keyboard"; import { translate } from "../../../../../components/translate"; -import { - DismissKeyboard, - useSoftKeyboardEffect, -} from "../../../../../components/keyboard"; const EditUser = observer(() => { const toast = useToastController(); @@ -37,13 +35,9 @@ const EditUser = observer(() => { validate={(values) => { const errors: Record = {}; if (!values.firstName) { - errors.firstName = translate.t( - "account.edituser.errors.firstname.required" - ); + errors.firstName = translate.t("account.edituser.errors.firstname.required"); } else if (!values.lastName) { - errors.lastName = translate.t( - "account.edituser.errors.lastname.required" - ); + errors.lastName = translate.t("account.edituser.errors.lastname.required"); } return errors; }} @@ -52,7 +46,7 @@ const EditUser = observer(() => { await AuthStore.updateUser({ firstName: values.firstName, lastName: values.lastName, - avatarUrl: values.avatarUrl || "", + avatarUrl: values.avatarUrl ?? "", }); await AuthStore.loadUser(); toast.show(translate.t("account.edituser.success"), { @@ -68,16 +62,8 @@ const EditUser = observer(() => { setSubmitting(false); }} > - {({ - values, - errors, - handleChange, - handleBlur, - handleSubmit, - setFieldValue, - isSubmitting, - }) => ( - + {({ values, errors, handleChange, handleBlur, handleSubmit, setFieldValue, isSubmitting }) => ( + {translate.t("account.edituser.subHeadline")} {translate.t("account.edituser.title")} @@ -87,9 +73,9 @@ const EditUser = observer(() => { {translate.t("account.edituser.avatarurl")} { const result = await ImagePicker.launchImageLibraryAsync({ @@ -101,17 +87,14 @@ const EditUser = observer(() => { }); if (!result.canceled) { - setFieldValue( - "avatarUrl", - `data:image/jpeg;base64,${result.assets[0].base64}` - ); + void setFieldValue("avatarUrl", `data:image/jpeg;base64,${result.assets[0]?.base64}`); } }} - pressStyle={{ bg: "$gray3" }} + pressStyle={{ backgroundColor: "$gray3" }} borderColor="$gray10" borderWidth="$0.5" borderStyle="dashed" - p="$1.5" + padding="$1.5" > {values.avatarUrl ? ( <> @@ -120,14 +103,14 @@ const EditUser = observer(() => { overflow="hidden" position="absolute" right={"$-2.5"} - bg={"$gray5"} + backgroundColor={"$gray5"} top={"$-2.5"} zIndex={2} - p="$1.5" + padding="$1.5" onPress={() => { - setFieldValue("avatarUrl", ""); + void setFieldValue("avatarUrl", ""); }} - pressStyle={{ bg: "$gray3" }} + pressStyle={{ backgroundColor: "$gray3" }} > @@ -144,19 +127,9 @@ const EditUser = observer(() => { /> ) : ( - + - + {translate.t("account.edituser.uploadavatarurl")} @@ -186,10 +159,8 @@ const EditUser = observer(() => { /> - {(errors.firstName || errors.lastName) && ( - - {errors.firstName || errors.lastName} - + {(errors.firstName ?? errors.lastName) && ( + {errors.firstName ?? errors.lastName} )} handleSubmit()} loading={isSubmitting}> diff --git a/apps/expo/app/(app)/(authorized)/home/index.tsx b/apps/expo/app/(app)/(authorized)/home/index.tsx index 0e87795..f54af87 100644 --- a/apps/expo/app/(app)/(authorized)/home/index.tsx +++ b/apps/expo/app/(app)/(authorized)/home/index.tsx @@ -1,16 +1,18 @@ +import { useState } from "react"; +import { isAxiosError } from "axios"; import { observer } from "mobx-react-lite"; -import { Button, Text, View, YStack } from "tamagui"; -import { Rest } from "../../../../components/clients/rest.client"; +import { Button, Text, YStack } from "tamagui"; + import { AuthStore } from "../../../../components/auth/auth.store"; -import { useState } from "react"; +import { Rest } from "../../../../components/clients/rest.client"; const Home = observer(() => { const [adminResponse, setAdminResponse] = useState(""); return ( - + @@ -144,5 +136,5 @@ export default function SignIn() {
- ) + ); } diff --git a/apps/expo/app/(app)/auth/signup/index.tsx b/apps/expo/app/(app)/auth/signup/index.tsx index aac6dab..5ee9c06 100644 --- a/apps/expo/app/(app)/auth/signup/index.tsx +++ b/apps/expo/app/(app)/auth/signup/index.tsx @@ -1,26 +1,16 @@ +import type { Href } from "expo-router"; +import { Link, router, Stack } from "expo-router"; import { LmButton } from "@tamagui-extras/core"; import { LmInput } from "@tamagui-extras/form"; import { Check } from "@tamagui/lucide-icons"; -import { Href, Link, Stack, router } from "expo-router"; import { Formik } from "formik"; -import React from "react"; -import { - Button, - Checkbox, - Heading, - Input, - Label, - ScrollView, - Text, - View, - XStack, - YStack, -} from "tamagui"; +import { Button, Checkbox, Heading, Input, ScrollView, Text, View, XStack, YStack } from "tamagui"; + import { AuthError } from "../../../../components/auth/auth.error"; import { AuthStore } from "../../../../components/auth/auth.store"; import { SocialProviders } from "../../../../components/auth/socialproviders"; -import { translate } from "../../../../components/translate"; import { useSoftKeyboardEffect } from "../../../../components/keyboard"; +import { translate } from "../../../../components/translate"; import { appConfig } from "../../../../constants/app.config"; export default function SignUp() { @@ -48,30 +38,18 @@ export default function SignUp() { const errors: Record = {}; if (!values.email) { errors.email = translate.t("auth.signUp.errors.email.required"); - } else if ( - !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email) - ) { + } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) { errors.email = translate.t("auth.signUp.errors.email.invalid"); } else if (!values.password || !values.passwordConfirm) { - errors.password = translate.t( - "auth.signUp.errors.password.required" - ); + errors.password = translate.t("auth.signUp.errors.password.required"); } else if (!values.firstName) { - errors.firstName = translate.t( - "auth.signUp.errors.firstName.required" - ); + errors.firstName = translate.t("auth.signUp.errors.firstName.required"); } else if (!values.lastName) { - errors.lastName = translate.t( - "auth.signUp.errors.lastName.required" - ); + errors.lastName = translate.t("auth.signUp.errors.lastName.required"); } else if (!(values.password === values.passwordConfirm)) { - errors.password = translate.t( - "auth.signUp.errors.passwordConfirm.invalid" - ); + errors.password = translate.t("auth.signUp.errors.passwordConfirm.invalid"); } else if (!values.privacyPolicy) { - errors.privacyPolicy = translate.t( - "auth.signUp.errors.privacyPolicy.required" - ); + errors.privacyPolicy = translate.t("auth.signUp.errors.privacyPolicy.required"); } console.log(errors); return errors; @@ -91,6 +69,7 @@ export default function SignUp() { const errors = error.formFields.map((field) => ({ [field.id]: field.error, })); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument setErrors(Object.assign({}, ...errors)); } else { setErrors({ @@ -104,16 +83,8 @@ export default function SignUp() { setSubmitting(false); }} > - {({ - values, - errors, - handleChange, - handleBlur, - handleSubmit, - setFieldValue, - isSubmitting, - }) => ( - + {({ values, errors, handleChange, handleBlur, handleSubmit, setFieldValue, isSubmitting }) => ( + {translate.t("auth.signUp.subHeadline")} {translate.t("auth.signUp.title")} @@ -173,9 +144,9 @@ export default function SignUp() { { - setFieldValue("privacyPolicy", value); + void setFieldValue("privacyPolicy", value); }} id="checkbox-privacypolicy" > @@ -183,44 +154,36 @@ export default function SignUp() { - - - - - - {errors.firstName || - errors.lastName || - errors.privacyPolicy || - errors.email || - errors.password} - + + {translate.t("auth.signUp.privacyPolicy.4" as "auth.signUp.privacyPolicy")} + + {JSON.stringify(errors) !== "{}" && ( + + + {errors.firstName ?? errors.lastName ?? errors.privacyPolicy ?? errors.email ?? errors.password} + + + )} handleSubmit()} loading={isSubmitting}> @@ -228,13 +191,13 @@ export default function SignUp() { diff --git a/apps/expo/app/+html.tsx b/apps/expo/app/+html.tsx index 25524d7..dae48be 100644 --- a/apps/expo/app/+html.tsx +++ b/apps/expo/app/+html.tsx @@ -1,4 +1,4 @@ -import { ScrollViewStyleReset } from 'expo-router/html'; +import { ScrollViewStyleReset } from "expo-router/html"; // This file is web-only and used to configure the root HTML for every // web page during static rendering. diff --git a/apps/expo/app/[...missing].tsx b/apps/expo/app/[...missing].tsx index a9bd75e..f55175d 100644 --- a/apps/expo/app/[...missing].tsx +++ b/apps/expo/app/[...missing].tsx @@ -1,5 +1,6 @@ -import { Link, Stack, useSegments } from "expo-router"; +import { Link, Stack } from "expo-router"; import { Button, Heading, View } from "tamagui"; + import { AuthStore } from "../components/auth/auth.store"; import { translate } from "../components/translate"; @@ -7,7 +8,7 @@ export default function NotFoundScreen() { return ( <> - + {translate.t("notFound.description")} diff --git a/apps/expo/app/_layout.tsx b/apps/expo/app/_layout.tsx index 42c6930..516eaf5 100644 --- a/apps/expo/app/_layout.tsx +++ b/apps/expo/app/_layout.tsx @@ -1,17 +1,16 @@ -import { - DarkTheme, - DefaultTheme, - ThemeProvider, -} from "@react-navigation/native"; -import { ToastProvider, ToastViewport } from "@tamagui/toast"; -import { useFonts } from "expo-font"; -import { SplashScreen, Stack } from "expo-router"; import React, { useEffect, useRef, useState } from "react"; import { useColorScheme } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useFonts } from "expo-font"; +import { SplashScreen, Stack } from "expo-router"; +import { DarkTheme, DefaultTheme, ThemeProvider } from "@react-navigation/native"; +import { ToastProvider, ToastViewport } from "@tamagui/toast"; import { TamaguiProvider, Theme } from "tamagui"; + import { translate } from "../components/translate"; + import "../supertoken.config"; + import config from "../tamagui.config"; export { ErrorBoundary } from "expo-router"; @@ -25,7 +24,9 @@ SplashScreen.preventAutoHideAsync(); export default function RootLayout() { const [loaded, error] = useFonts({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment InterBold: require("@tamagui/font-inter/otf/Inter-Bold.otf"), }); @@ -54,29 +55,21 @@ function RootLayoutNav() { useEffect(() => { if (colorScheme !== currentColorScheme) { - onColorSchemeChange.current = setTimeout( - () => setCurrentColorScheme(colorScheme), - 1000 - ); + onColorSchemeChange.current = setTimeout(() => setCurrentColorScheme(colorScheme), 1000); } else if (onColorSchemeChange.current) { clearTimeout(onColorSchemeChange.current); } }, [colorScheme]); useEffect(() => { - translate.init(); + void translate.init(); }, []); return ( - + - + @@ -89,12 +82,5 @@ function RootLayoutNav() { const SafeToastViewport = () => { const { left, top, right } = useSafeAreaInsets(); - return ( - - ); + return ; }; diff --git a/apps/expo/babel.config.js b/apps/expo/babel.config.js index 06eb19a..c9e3c15 100644 --- a/apps/expo/babel.config.js +++ b/apps/expo/babel.config.js @@ -1,29 +1,29 @@ module.exports = function (api) { - api.cache(true) + api.cache(true); return { - presets: ['babel-preset-expo'], + presets: ["babel-preset-expo"], plugins: [ // Required for expo-router - 'expo-router/babel', + "expo-router/babel", // NOTE: this is required to pass the right environment [ - 'transform-inline-environment-variables', + "transform-inline-environment-variables", // NOTE: include is optional, you can leave this part out { - include: ['EXPO_ROUTER_APP_ROOT'], + include: ["EXPO_ROUTER_APP_ROOT"], }, ], // NOTE: this is optional, you don't *need* the compiler [ - '@tamagui/babel-plugin', + "@tamagui/babel-plugin", { - components: ['tamagui', '@tamagui-extras/core', '@tamagui-extras/form'], - config: './tamagui.config.ts', + components: ["tamagui", "@tamagui-extras/core", "@tamagui-extras/form"], + config: "./tamagui.config.ts", logTimings: true, }, ], // NOTE: this is only necessary if you are using reanimated for animations - 'react-native-reanimated/plugin', + "react-native-reanimated/plugin", ], - } -} + }; +}; diff --git a/apps/expo/components/auth/apple.button.tsx b/apps/expo/components/auth/apple.button.tsx index e94b94d..2b54d26 100644 --- a/apps/expo/components/auth/apple.button.tsx +++ b/apps/expo/components/auth/apple.button.tsx @@ -1,10 +1,13 @@ import { LmButton } from "@tamagui-extras/core"; -import {} from "@tamagui/lucide-icons"; + +import "@tamagui/lucide-icons"; + import { useState } from "react"; import { Path, Svg } from "react-native-svg"; +import { router } from "expo-router"; + import { translate } from "../translate"; import { AuthStore } from "./auth.store"; -import { router } from "expo-router"; export const AppleButton = () => { const [loading, setLoading] = useState(false); diff --git a/apps/expo/components/auth/auth.error.ts b/apps/expo/components/auth/auth.error.ts index d054852..0bd050b 100644 --- a/apps/expo/components/auth/auth.error.ts +++ b/apps/expo/components/auth/auth.error.ts @@ -1,4 +1,4 @@ -import { FormFieldId } from "./form/interfaces"; +import type { FormFieldId } from "./form/interfaces"; export type AuthErrorType = | "FIELD_ERROR" diff --git a/apps/expo/components/auth/auth.provider.tsx b/apps/expo/components/auth/auth.provider.tsx index 3292c83..3bbcb31 100644 --- a/apps/expo/components/auth/auth.provider.tsx +++ b/apps/expo/components/auth/auth.provider.tsx @@ -1,36 +1,33 @@ -import { Slot, Stack, router, useSegments } from "expo-router"; -import { observer } from "mobx-react-lite"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { ActivityIndicator } from "react-native"; +import { router, useSegments } from "expo-router"; +import { observer } from "mobx-react-lite"; import { View } from "tamagui"; + import { AuthStore } from "./auth.store"; -export const AuthProvider = observer( - ({ children }: { children: JSX.Element }) => { - const segments = useSegments(); - console.log(`Navigated to ${segments.join("/")}`); - const publicRoute = - AuthStore.publicRoutes.some((publicRoute) => - (segments as string[]).includes(publicRoute) - ) || segments.length === 0; - useEffect(() => { - if (!AuthStore.loaded) { - void AuthStore.init().then(() => { - // needs to be called after the router is initialized - if (AuthStore.isLoggedIn && publicRoute) { - router.replace("/(app)/(authorized)/home"); - } else if (!AuthStore.isLoggedIn) { - router.replace("/auth/signin"); - } - }); - } - }, []); - return AuthStore.loaded && (AuthStore.isLoggedIn || publicRoute) ? ( - children - ) : ( - - - - ); - } -); +export const AuthProvider = observer(({ children }: { children: JSX.Element }) => { + const segments = useSegments(); + console.log(`Navigated to ${segments.join("/")}`); + const publicRoute = + AuthStore.publicRoutes.some((publicRoute) => (segments as string[]).includes(publicRoute)) || segments.length === 0; + useEffect(() => { + if (!AuthStore.loaded) { + void AuthStore.init().then(() => { + // needs to be called after the router is initialized + if (AuthStore.isLoggedIn && publicRoute) { + router.replace("/(app)/(authorized)/home"); + } else if (!AuthStore.isLoggedIn) { + router.replace("/auth/signin"); + } + }); + } + }, []); + return AuthStore.loaded && (AuthStore.isLoggedIn || publicRoute) ? ( + children + ) : ( + + + + ); +}); diff --git a/apps/expo/components/auth/auth.service.ts b/apps/expo/components/auth/auth.service.ts index 46e27c6..c2edce9 100644 --- a/apps/expo/components/auth/auth.service.ts +++ b/apps/expo/components/auth/auth.service.ts @@ -1,8 +1,9 @@ import { makeRedirectUri } from "expo-auth-session"; import SuperTokens from "supertokens-react-native"; + import { appConfig } from "../../constants/app.config"; import { Rest } from "../clients/rest.client"; -import { FormFieldId, IFormField } from "./form/interfaces"; +import type { FormFieldId, IFormField } from "./form/interfaces"; export class AuthService { public async getUser() { @@ -18,7 +19,7 @@ export class AuthService { avatarUrl?: string; }; roles: string[]; - session: { userId: string; thirdParty?: { id: string; userId: string } }; + session: { userId: string; thirdParty: { id: string; userId: string }[] }; }>("/auth/session"); } @@ -150,26 +151,29 @@ export class AuthService { return SuperTokens.signOut(); } - public async changePassword({ - oldPassword, - newPassword, - }: { - oldPassword: string; - newPassword: string; - }) { - return Rest.client.post(`/auth/change-password`, { + public async changePassword({ oldPassword, newPassword }: { oldPassword: string; newPassword: string }) { + return Rest.client.post<{ + status: "OK" | "GENERAL_ERROR"; + formFields?: { error: string; id: FormFieldId }[]; + }>(`/auth/change-password`, { oldPassword, newPassword, }); } public async changeEmail({ email }: { email: string }) { - return Rest.client.post(`/auth/change-email`, { + return Rest.client.post<{ + status: "OK" | "GENERAL_ERROR"; + formFields?: { error: string; id: FormFieldId }[]; + }>(`/auth/change-email`, { email, }); } public async deleteAccount() { - return Rest.client.post(`/auth/delete-account`); + return Rest.client.post<{ + status: "OK" | "GENERAL_ERROR"; + formFields?: { error: string; id: FormFieldId }[]; + }>(`/auth/delete-account`); } } diff --git a/apps/expo/components/auth/auth.store.ts b/apps/expo/components/auth/auth.store.ts index 383cae1..5defc32 100644 --- a/apps/expo/components/auth/auth.store.ts +++ b/apps/expo/components/auth/auth.store.ts @@ -1,7 +1,8 @@ -import { isAxiosError } from "axios"; import * as AppleAuthentication from "expo-apple-authentication"; import { router } from "expo-router"; +import { isAxiosError } from "axios"; import { makeAutoObservable } from "mobx"; + import { AuthError } from "./auth.error"; import { AuthService } from "./auth.service"; @@ -11,10 +12,10 @@ interface IUser { lastName: string; email: string; avatarUrl?: string; - thirdParty?: { + thirdParty: { id: string; userId: string; - }; + }[]; roles: string[]; } @@ -67,6 +68,7 @@ class AuthStoreSingleton { }; } + // eslint-disable-next-line @typescript-eslint/adjacent-overload-signatures public get user(): IUser { if (!this._user) { throw new Error("no user"); @@ -143,12 +145,7 @@ class AuthStoreSingleton { await this.loadUser(); } - public async signUp(formData: { - email: string; - password: string; - firstName: string; - lastName: string; - }) { + public async signUp(formData: { email: string; password: string; firstName: string; lastName: string }) { const response = await this.authService.signUp([ { id: "email", @@ -177,9 +174,7 @@ class AuthStoreSingleton { } public async resetPassword({ email }: { email: string }) { - const response = await this.authService.resetPassword([ - { id: "email", value: email }, - ]); + const response = await this.authService.resetPassword([{ id: "email", value: email }]); if (response.data.status === "FIELD_ERROR") { throw new AuthError(response.data.status, response.data.formFields); } @@ -189,19 +184,13 @@ class AuthStoreSingleton { await this.authService.verifyEmail(); } - public async changePassword({ - oldPassword, - newPassword, - }: { - oldPassword: string; - newPassword: string; - }) { + public async changePassword({ oldPassword, newPassword }: { oldPassword: string; newPassword: string }) { const response = await this.authService.changePassword({ oldPassword, newPassword, }); if (response.data.status !== "OK") { - throw new AuthError(response.data.status, response.data.formFields); + throw new AuthError(response.data.status, response.data?.formFields); } } @@ -210,7 +199,7 @@ class AuthStoreSingleton { email, }); if (response.data.status !== "OK") { - throw new AuthError(response.data.status, response.data.formFields); + throw new AuthError(response.data.status, response.data?.formFields); } } diff --git a/apps/expo/components/auth/form/interfaces.ts b/apps/expo/components/auth/form/interfaces.ts index 6a1dc92..a5e31a8 100644 --- a/apps/expo/components/auth/form/interfaces.ts +++ b/apps/expo/components/auth/form/interfaces.ts @@ -1,9 +1,4 @@ -export type FormFieldId = - | "email" - | "password" - | "first_name" - | "last_name" - | "avatarUrl"; +export type FormFieldId = "email" | "password" | "first_name" | "last_name" | "avatarUrl"; export interface IFormField { id: T; diff --git a/apps/expo/components/auth/github.button.tsx b/apps/expo/components/auth/github.button.tsx index c8ebdab..031ed25 100644 --- a/apps/expo/components/auth/github.button.tsx +++ b/apps/expo/components/auth/github.button.tsx @@ -1,51 +1,52 @@ -import { LmButton } from '@tamagui-extras/core' -import { makeRedirectUri, useAuthRequest } from 'expo-auth-session' -import { router } from 'expo-router' -import * as WebBrowser from 'expo-web-browser' -import * as React from 'react' -import { Path, Svg } from 'react-native-svg' -import { translate } from '../translate' -import { AuthStore } from './auth.store' -import { appConfig } from '../../constants/app.config' +import * as React from "react"; +import { Path, Svg } from "react-native-svg"; +import { makeRedirectUri, useAuthRequest } from "expo-auth-session"; +import { router } from "expo-router"; +import * as WebBrowser from "expo-web-browser"; +import { LmButton } from "@tamagui-extras/core"; -WebBrowser.maybeCompleteAuthSession() +import { appConfig } from "../../constants/app.config"; +import { translate } from "../translate"; +import { AuthStore } from "./auth.store"; -const CLIENT_ID = process.env.EXPO_PUBLIC_GITHUB_CLIENT_ID as string +WebBrowser.maybeCompleteAuthSession(); + +const CLIENT_ID = process.env.EXPO_PUBLIC_GITHUB_CLIENT_ID!; const discovery = { - authorizationEndpoint: 'https://github.com/login/oauth/authorize', - tokenEndpoint: 'https://github.com/login/oauth/access_token', + authorizationEndpoint: "https://github.com/login/oauth/authorize", + tokenEndpoint: "https://github.com/login/oauth/access_token", revocationEndpoint: `https://github.com/settings/connections/applications/${CLIENT_ID}`, -} +}; export const GithubButton = () => { - const [loading, setLoading] = React.useState(false) + const [loading, setLoading] = React.useState(false); const [_request, _response, promptAsync] = useAuthRequest( { clientId: CLIENT_ID, - scopes: ['read:user', 'user:email'], + scopes: ["read:user", "user:email"], redirectUri: makeRedirectUri({ scheme: appConfig.bundleIdentifier, }), }, discovery - ) + ); return ( { - setLoading(true) + setLoading(true); try { - const response = await promptAsync() - if (response.type === 'success') { - const { code } = response.params - await AuthStore.signInWithGithub({ code }) - router.replace('/(app)/(authorized)/home') + const response = await promptAsync(); + if (response.type === "success") { + const { code } = response.params; + await AuthStore.signInWithGithub({ code }); + router.replace("/(app)/(authorized)/home"); } else { - console.error(response) + console.error(response); } } catch (error) { - console.log(JSON.stringify(error)) + console.log(JSON.stringify(error)); } - setLoading(false) + setLoading(false); }} icon={ @@ -56,7 +57,7 @@ export const GithubButton = () => { } > - {translate.t('auth.login.github')} + {translate.t("auth.login.github")} - ) -} + ); +}; diff --git a/apps/expo/components/auth/google.button.tsx b/apps/expo/components/auth/google.button.tsx index 735917b..fe57953 100644 --- a/apps/expo/components/auth/google.button.tsx +++ b/apps/expo/components/auth/google.button.tsx @@ -1,10 +1,11 @@ -import { LmButton } from "@tamagui-extras/core"; import { useState } from "react"; import { Path, Svg } from "react-native-svg"; -import { translate } from "../translate"; +import { router } from "expo-router"; import { GoogleSignin } from "@react-native-google-signin/google-signin"; +import { LmButton } from "@tamagui-extras/core"; + +import { translate } from "../translate"; import { AuthStore } from "./auth.store"; -import { router } from "expo-router"; GoogleSignin.configure(); diff --git a/apps/expo/components/auth/socialproviders.tsx b/apps/expo/components/auth/socialproviders.tsx index 76a35ea..8667435 100644 --- a/apps/expo/components/auth/socialproviders.tsx +++ b/apps/expo/components/auth/socialproviders.tsx @@ -1,4 +1,5 @@ -import { YStack, XStack, Separator, Text } from "tamagui"; +import { Separator, Text, XStack, YStack } from "tamagui"; + import { AppleButton } from "./apple.button"; import { GithubButton } from "./github.button"; import { GoogleButton } from "./google.button"; diff --git a/apps/expo/components/clients/rest.client.ts b/apps/expo/components/clients/rest.client.ts index 9df6674..627d162 100644 --- a/apps/expo/components/clients/rest.client.ts +++ b/apps/expo/components/clients/rest.client.ts @@ -1,10 +1,15 @@ import "react-native-url-polyfill/auto"; -import axios, { AxiosInstance } from "axios"; + +import type { AxiosInstance } from "axios"; +import axios from "axios"; import SuperTokens from "supertokens-react-native"; + +import type { AuthErrorType } from "../auth/auth.error"; import { AuthError } from "../auth/auth.error"; +import type { FormFieldId } from "../auth/form/interfaces"; class RestClient { - public readonly API_URL = process.env.EXPO_PUBLIC_API_URL as string; + public readonly API_URL = process.env.EXPO_PUBLIC_API_URL!; public client: AxiosInstance; constructor() { @@ -17,15 +22,21 @@ class RestClient { SuperTokens.addAxiosInterceptors(this.client); this.client.interceptors.response.use((response) => { console.log( - `${response.config.method?.toUpperCase()} ${response.config.url} - ${ - response.status - } - ${JSON.stringify(response.data, null, 4).substring(0, 1000)}` + `${response.config.method?.toUpperCase()} ${response.config.url} - ${response.status} - ${JSON.stringify( + response.data, + null, + 4 + ).substring(0, 1000)}` ); // supertokens responses with 200 even if the request failed - if (response.data?.status && response.data.status !== "OK") { + const supertokensError = response.data as { + status?: AuthErrorType | "OK"; + formFields?: { error: string; id: FormFieldId }[]; + }; + if (supertokensError.status && supertokensError.status !== "OK") { throw new AuthError( - response.data.status, - "formFields" in response.data ? response.data.formFields : undefined + supertokensError.status, + "formFields" in supertokensError ? supertokensError.formFields : undefined ); } return response; diff --git a/apps/expo/components/keyboard.tsx b/apps/expo/components/keyboard.tsx index 731f935..763296a 100644 --- a/apps/expo/components/keyboard.tsx +++ b/apps/expo/components/keyboard.tsx @@ -1,9 +1,9 @@ -import { useFocusEffect } from "@react-navigation/native"; -import Constants from "expo-constants"; import { Keyboard, TouchableWithoutFeedback } from "react-native"; +import Constants, { AppOwnership } from "expo-constants"; +import { useFocusEffect } from "@react-navigation/native"; import { View } from "tamagui"; -const isRunningInExpoGo = Constants.appOwnership === "expo"; +const isRunningInExpoGo = Constants.appOwnership === AppOwnership.Expo; /** * This hook should be used in every screen that has a form input field to avoid the keyboard @@ -12,8 +12,11 @@ const isRunningInExpoGo = Constants.appOwnership === "expo"; export const useSoftKeyboardEffect = () => { if (isRunningInExpoGo) return; + // eslint-disable-next-line react-hooks/rules-of-hooks useFocusEffect(() => { - const reactNativeAvoidSoftinput = require("react-native-avoid-softinput"); + const reactNativeAvoidSoftinput = + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("react-native-avoid-softinput") as typeof import("react-native-avoid-softinput"); const { AvoidSoftInput } = reactNativeAvoidSoftinput; AvoidSoftInput.setShouldMimicIOSBehavior(true); AvoidSoftInput.setEnabled(true); diff --git a/apps/expo/components/translate.ts b/apps/expo/components/translate.ts index 1e386bc..edb1cf8 100644 --- a/apps/expo/components/translate.ts +++ b/apps/expo/components/translate.ts @@ -1,12 +1,13 @@ import * as Localization from "expo-localization"; +import i18next from "i18next"; + import de from "../constants/translation/de.json"; import en from "../constants/translation/en.json"; -import i18next from "i18next"; export const translate = { t: i18next.t, - init: () => { - i18next.init({ + init: async () => { + await i18next.init({ compatibilityJSON: "v3", lng: Localization.locale, fallbackLng: "en", diff --git a/apps/expo/i18next.d.ts b/apps/expo/i18next.d.ts index 47c7463..d044386 100644 --- a/apps/expo/i18next.d.ts +++ b/apps/expo/i18next.d.ts @@ -1,5 +1,6 @@ import "i18next"; -import de from "./constants/translation/de.json"; + +import type de from "./constants/translation/de.json"; declare module "i18next" { interface CustomTypeOptions { diff --git a/apps/expo/metro.config.js b/apps/expo/metro.config.js index dc7b19e..903252a 100644 --- a/apps/expo/metro.config.js +++ b/apps/expo/metro.config.js @@ -1,26 +1,26 @@ // Learn more https://docs.expo.io/guides/customizing-metro -const { getDefaultConfig } = require('expo/metro-config') -const path = require('path') +const { getDefaultConfig } = require("expo/metro-config"); +const path = require("path"); -const projectRoot = __dirname -const workspaceRoot = path.resolve(projectRoot, '../..') +const projectRoot = __dirname; +const workspaceRoot = path.resolve(projectRoot, "../.."); /** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname, { // [Web-only]: Enables CSS support in Metro. isCSSEnabled: true, -}) +}); if (config.resolver) { // 1. Watch all files within the monorepo - config.watchFolders = [workspaceRoot] + config.watchFolders = [workspaceRoot]; // 2. Let Metro know where to resolve packages and in what order config.resolver.nodeModulesPaths = [ - path.resolve(projectRoot, 'node_modules'), - path.resolve(workspaceRoot, 'node_modules'), - ] + path.resolve(projectRoot, "node_modules"), + path.resolve(workspaceRoot, "node_modules"), + ]; // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` - config.resolver.disableHierarchicalLookup = true - config.resolver.sourceExts.push('mjs') + config.resolver.disableHierarchicalLookup = true; + config.resolver.sourceExts.push("mjs"); } -module.exports = config +module.exports = config; diff --git a/apps/expo/package.json b/apps/expo/package.json index 2553e05..8df9681 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -10,7 +10,8 @@ "test": "jest --watchAll", "export": "expo export --platform web", "dev:client": "npx expo start -c --dev-client", - "lint": "echo \"No linting configured\"" + "lint": "eslint . --fix", + "format": "prettier --write \"**/*.{js,cjs,mjs,ts,tsx,md,json}\"" }, "jest": { "preset": "jest-expo" @@ -73,6 +74,9 @@ "tamagui": "^1.75.1" }, "devDependencies": { + "@acme/eslint-config": "^0.2.0", + "@acme/prettier-config": "^0.1.0", + "@acme/tsconfig": "^0.1.0", "@babel/core": "^7.23.2", "@types/react": "~18.2.31", "jest": "^29.7.0", @@ -85,5 +89,13 @@ "metro": "0.76.0", "metro-resolver": "0.76.0" }, + "eslintConfig": { + "root": true, + "extends": [ + "@acme/eslint-config/base", + "@acme/eslint-config/react" + ] + }, + "prettier": "@acme/prettier-config", "private": true } diff --git a/apps/expo/supertoken.config.ts b/apps/expo/supertoken.config.ts index 48412dd..ff3e8c9 100644 --- a/apps/expo/supertoken.config.ts +++ b/apps/expo/supertoken.config.ts @@ -1,4 +1,5 @@ import SuperTokens from "supertokens-react-native"; + import { Rest } from "./components/clients/rest.client"; SuperTokens.init({ diff --git a/apps/expo/tamagui.config.ts b/apps/expo/tamagui.config.ts index b102415..654f9f8 100644 --- a/apps/expo/tamagui.config.ts +++ b/apps/expo/tamagui.config.ts @@ -87,6 +87,6 @@ declare module "tamagui" { // work everywhere you import `tamagui` - interface TamaguiCustomConfig extends AppConfig {} + type TamaguiCustomConfig = AppConfig } export default config; diff --git a/apps/expo/tsconfig.json b/apps/expo/tsconfig.json index 75edaf8..f103739 100644 --- a/apps/expo/tsconfig.json +++ b/apps/expo/tsconfig.json @@ -1,12 +1,7 @@ { - "extends": "expo/tsconfig.base", + "extends": ["expo/tsconfig.base", "@acme/tsconfig/base"], "compilerOptions": { - "strict": true, + "strict": true }, - "include": [ - "**/*.ts", - "**/*.tsx", - ".expo/types/**/*.ts", - "expo-env.d.ts" - ] + "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] } diff --git a/package.json b/package.json index 43859ea..b49f26e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "api": "pnpm run --filter @acme/api dev", "fix": "manypkg fix", "lint": "pnpm run --filter \"{apps/*}\" --stream --parallel lint", + "format": "pnpm run --filter \"{apps/*}\" --stream --parallel format", "build": "pnpm run --filter \"{apps/*}\" --stream --parallel build", "build:api": "pnpm run --filter @acme/api build", "start:prod": "node dist/main", @@ -23,9 +24,7 @@ "@babel/runtime": "^7.23.2", "@manypkg/cli": "^0.21.0", "check-dependency-version-consistency": "^4.1.0", - "eslint": "^8.52.0", "node-gyp": "^9.4.0", - "prettier": "^3.0.3", "turbo": "^1.10.16", "typescript": "^5.2.2" } diff --git a/packages/eslint-config/base.js b/packages/eslint-config/base.js new file mode 100644 index 0000000..7ee1cf9 --- /dev/null +++ b/packages/eslint-config/base.js @@ -0,0 +1,33 @@ +/** @type {import("eslint").Linter.Config} */ +const config = { + extends: [ + "turbo", + "eslint:recommended", + "plugin:@typescript-eslint/recommended-type-checked", + "plugin:@typescript-eslint/stylistic-type-checked", + "prettier", + ], + env: { + es2022: true, + node: true, + }, + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, + plugins: ["@typescript-eslint", "import"], + rules: { + "turbo/no-undeclared-env-vars": "off", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], + "@typescript-eslint/consistent-type-imports": [ + "warn", + { prefer: "type-imports", fixStyle: "separate-type-imports" }, + ], + "@typescript-eslint/no-misused-promises": [2, { checksVoidReturn: { attributes: false } }], + "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + }, + ignorePatterns: ["**/.eslintrc.cjs", "**/*.config.js", "**/*.config.cjs", ".next", "dist", "pnpm-lock.yaml"], + reportUnusedDisableDirectives: true, +}; + +module.exports = config; diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json new file mode 100644 index 0000000..de571f4 --- /dev/null +++ b/packages/eslint-config/package.json @@ -0,0 +1,39 @@ +{ + "name": "@acme/eslint-config", + "version": "0.2.0", + "private": true, + "license": "MIT", + "files": [ + "./base.js", + "./react.js" + ], + "scripts": { + "clean": "rm -rf .turbo node_modules", + "lint": "eslint .", + "format": "prettier --check \"**/*.{mjs,ts,md,json}\"", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@types/eslint": "^8.44.4", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", + "eslint-config-prettier": "^9.0.0", + "eslint-config-turbo": "^1.10.15", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0" + }, + "devDependencies": { + "@acme/prettier-config": "^0.1.0", + "@acme/tsconfig": "^0.1.0", + "eslint": "^8.51.0" + }, + "eslintConfig": { + "root": true, + "extends": [ + "./base.js" + ] + }, + "prettier": "@acme/prettier-config" +} diff --git a/packages/eslint-config/react.js b/packages/eslint-config/react.js new file mode 100644 index 0000000..ee21a21 --- /dev/null +++ b/packages/eslint-config/react.js @@ -0,0 +1,20 @@ +/** @type {import('eslint').Linter.Config} */ +const config = { + extends: ["plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:jsx-a11y/recommended"], + rules: { + "react/prop-types": "off", + }, + globals: { + React: "writable", + }, + settings: { + react: { + version: "detect", + }, + }, + env: { + browser: true, + }, +}; + +module.exports = config; diff --git a/packages/eslint-config/tsconfig.json b/packages/eslint-config/tsconfig.json new file mode 100644 index 0000000..ccd6c8c --- /dev/null +++ b/packages/eslint-config/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@acme/tsconfig/base", + "compilerOptions": { + "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" + }, + "include": ["."], + "exclude": ["node_modules"] +} diff --git a/packages/prettier-config/config.mjs b/packages/prettier-config/config.mjs new file mode 100644 index 0000000..128c3d9 --- /dev/null +++ b/packages/prettier-config/config.mjs @@ -0,0 +1,30 @@ +import { fileURLToPath } from "url"; + +/** @typedef {import("prettier").Config} PrettierConfig */ +/** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */ +/** @typedef {import("@ianvs/prettier-plugin-sort-imports").PluginConfig} SortImportsConfig */ + +/** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */ +const config = { + plugins: [ + "@ianvs/prettier-plugin-sort-imports", + ], + importOrder: [ + "^(react/(.*)$)|^(react$)|^(react-native(.*)$)", + "^(next/(.*)$)|^(next$)", + "^(expo(.*)$)|^(expo$)", + "", + "", + "^@acme/(.*)$", + "", + "^~/", + "^[../]", + "^[./]", + ], + importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], + importOrderTypeScriptVersion: "4.4.0", + trailingComma: "es5", + printWidth: 120, +}; + +export default config; \ No newline at end of file diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json new file mode 100644 index 0000000..cc9fbf4 --- /dev/null +++ b/packages/prettier-config/package.json @@ -0,0 +1,20 @@ +{ + "name": "@acme/prettier-config", + "private": true, + "version": "0.1.0", + "main": "config.mjs", + "scripts": { + "clean": "rm -rf .turbo node_modules", + "format": "prettier --check \"**/*.{mjs,ts,md,json}\"", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.1.0", + "prettier": "^3.0.3" + }, + "devDependencies": { + "@acme/tsconfig": "^0.1.0", + "typescript": "^5.2.2" + }, + "prettier": "@acme/prettier-config" +} diff --git a/packages/prettier-config/tsconfig.json b/packages/prettier-config/tsconfig.json new file mode 100644 index 0000000..9f03ea1 --- /dev/null +++ b/packages/prettier-config/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@acme/tsconfig/base", + "compilerOptions": { + "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" + }, + "include": ["."], + "exclude": ["node_modules"] + } \ No newline at end of file diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json new file mode 100644 index 0000000..eb68382 --- /dev/null +++ b/packages/tsconfig/base.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "checkJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "noUncheckedIndexedAccess": true + }, + "exclude": ["node_modules", "build", "dist", ".next", ".expo"] +} diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json new file mode 100644 index 0000000..7efc278 --- /dev/null +++ b/packages/tsconfig/package.json @@ -0,0 +1,8 @@ +{ + "name": "@acme/tsconfig", + "private": true, + "version": "0.1.0", + "files": [ + "base.json" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1e3571..8eac870 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,15 +24,9 @@ importers: check-dependency-version-consistency: specifier: ^4.1.0 version: 4.1.0 - eslint: - specifier: ^8.52.0 - version: 8.52.0 node-gyp: specifier: ^9.4.0 version: 9.4.0 - prettier: - specifier: ^3.0.3 - version: 3.0.3 turbo: specifier: ^1.10.16 version: 1.10.16 @@ -85,6 +79,15 @@ importers: specifier: ^16.3.3 version: 16.3.3 devDependencies: + '@acme/eslint-config': + specifier: ^0.2.0 + version: link:../../packages/eslint-config + '@acme/prettier-config': + specifier: ^0.1.0 + version: link:../../packages/prettier-config + '@acme/tsconfig': + specifier: ^0.1.0 + version: link:../../packages/tsconfig '@nestjs/cli': specifier: ^10.2.0 version: 10.2.0 @@ -106,27 +109,12 @@ importers: '@types/supertest': specifier: ^2.0.15 version: 2.0.15 - '@typescript-eslint/eslint-plugin': - specifier: ^6.8.0 - version: 6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.52.0)(typescript@5.2.2) - '@typescript-eslint/parser': - specifier: ^6.8.0 - version: 6.8.0(eslint@8.52.0)(typescript@5.2.2) - eslint: - specifier: ^8.52.0 - version: 8.52.0 - eslint-config-prettier: - specifier: ^9.0.0 - version: 9.0.0(eslint@8.52.0) - eslint-plugin-prettier: - specifier: ^5.0.1 - version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.52.0)(prettier@3.0.3) + expo: + specifier: link:apps/expo + version: link:apps/expo jest: specifier: ^29.7.0 version: 29.7.0(@types/node@20.8.7)(ts-node@10.9.1) - prettier: - specifier: ^3.0.3 - version: 3.0.3 source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -317,6 +305,15 @@ importers: specifier: ^1.75.1 version: 1.75.1(@types/react@18.2.31)(react-dom@18.2.0)(react-native-web@0.19.6)(react-native@0.72.6)(react@18.2.0) devDependencies: + '@acme/eslint-config': + specifier: ^0.2.0 + version: link:../../packages/eslint-config + '@acme/prettier-config': + specifier: ^0.1.0 + version: link:../../packages/prettier-config + '@acme/tsconfig': + specifier: ^0.1.0 + version: link:../../packages/tsconfig '@babel/core': specifier: ^7.23.2 version: 7.23.2 @@ -336,6 +333,64 @@ importers: specifier: ^5.2.2 version: 5.2.2 + packages/eslint-config: + dependencies: + '@types/eslint': + specifier: ^8.44.4 + version: 8.44.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.8.0 + version: 6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/parser': + specifier: ^6.8.0 + version: 6.8.0(eslint@8.52.0)(typescript@5.2.2) + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.0.0(eslint@8.52.0) + eslint-config-turbo: + specifier: ^1.10.15 + version: 1.10.15(eslint@8.52.0) + eslint-plugin-import: + specifier: ^2.28.1 + version: 2.28.1(@typescript-eslint/parser@6.8.0)(eslint@8.52.0) + eslint-plugin-jsx-a11y: + specifier: ^6.7.1 + version: 6.7.1(eslint@8.52.0) + eslint-plugin-react: + specifier: ^7.33.2 + version: 7.33.2(eslint@8.52.0) + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.52.0) + devDependencies: + '@acme/prettier-config': + specifier: ^0.1.0 + version: link:../prettier-config + '@acme/tsconfig': + specifier: ^0.1.0 + version: link:../tsconfig + eslint: + specifier: ^8.51.0 + version: 8.52.0 + + packages/prettier-config: + dependencies: + '@ianvs/prettier-plugin-sort-imports': + specifier: ^4.1.0 + version: 4.1.0(prettier@3.0.3) + prettier: + specifier: ^3.0.3 + version: 3.0.3 + devDependencies: + '@acme/tsconfig': + specifier: ^0.1.0 + version: link:../tsconfig + typescript: + specifier: ^5.2.2 + version: 5.2.2 + + packages/tsconfig: {} + packages: /@aashutoshrathi/word-wrap@1.2.6: @@ -2303,7 +2358,7 @@ packages: getenv: 1.0.0 glob: 7.1.6 resolve-from: 5.0.0 - semver: 7.5.3 + semver: 7.5.4 slash: 3.0.0 xcode: 3.0.1 xml2js: 0.6.0 @@ -2728,6 +2783,26 @@ packages: /@humanwhocodes/object-schema@2.0.1: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + /@ianvs/prettier-plugin-sort-imports@4.1.0(prettier@3.0.3): + resolution: {integrity: sha512-IAXeTLU24k6mRPa6mFbW1qZJ/j0m3OeH44wyijWyr+YqqdNtBnfHxAntOAATS9iDfrT01NesKGsdzqnXdDQa/A==} + peerDependencies: + '@vue/compiler-sfc': '>=3.0.0' + prettier: 2 || 3 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + dependencies: + '@babel/core': 7.23.2 + '@babel/generator': 7.23.0 + '@babel/parser': 7.23.0 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 + prettier: 3.0.3 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: false + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -3393,18 +3468,6 @@ packages: requiresBuild: true optional: true - /@pkgr/utils@2.4.2: - resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dependencies: - cross-spawn: 7.0.3 - fast-glob: 3.3.1 - is-glob: 4.0.3 - open: 9.1.0 - picocolors: 1.0.0 - tslib: 2.6.2 - dev: true - /@radix-ui/react-compose-refs@1.0.0(react@18.2.0): resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} peerDependencies: @@ -5155,11 +5218,9 @@ packages: dependencies: '@types/estree': 1.0.3 '@types/json-schema': 7.0.14 - dev: true /@types/estree@1.0.3: resolution: {integrity: sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==} - dev: true /@types/express-serve-static-core@4.17.39: resolution: {integrity: sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==} @@ -5241,6 +5302,10 @@ packages: /@types/json-schema@7.0.14: resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: false + /@types/jsonwebtoken@9.0.2: resolution: {integrity: sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==} dependencies: @@ -5302,7 +5367,7 @@ packages: /@types/semver@7.5.4: resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} - dev: true + dev: false /@types/send@0.17.3: resolution: {integrity: sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==} @@ -5389,7 +5454,7 @@ packages: typescript: 5.2.2 transitivePeerDependencies: - supports-color - dev: true + dev: false /@typescript-eslint/parser@6.8.0(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==} @@ -5410,7 +5475,7 @@ packages: typescript: 5.2.2 transitivePeerDependencies: - supports-color - dev: true + dev: false /@typescript-eslint/scope-manager@6.8.0: resolution: {integrity: sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==} @@ -5418,7 +5483,7 @@ packages: dependencies: '@typescript-eslint/types': 6.8.0 '@typescript-eslint/visitor-keys': 6.8.0 - dev: true + dev: false /@typescript-eslint/type-utils@6.8.0(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==} @@ -5438,12 +5503,12 @@ packages: typescript: 5.2.2 transitivePeerDependencies: - supports-color - dev: true + dev: false /@typescript-eslint/types@6.8.0: resolution: {integrity: sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==} engines: {node: ^16.0.0 || >=18.0.0} - dev: true + dev: false /@typescript-eslint/typescript-estree@6.8.0(typescript@5.2.2): resolution: {integrity: sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==} @@ -5464,7 +5529,7 @@ packages: typescript: 5.2.2 transitivePeerDependencies: - supports-color - dev: true + dev: false /@typescript-eslint/utils@6.8.0(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==} @@ -5483,7 +5548,7 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true + dev: false /@typescript-eslint/visitor-keys@6.8.0: resolution: {integrity: sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==} @@ -5491,7 +5556,7 @@ packages: dependencies: '@typescript-eslint/types': 6.8.0 eslint-visitor-keys: 3.4.3 - dev: true + dev: false /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -5877,9 +5942,33 @@ packages: tslib: 2.6.2 dev: false + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: false + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.5 + is-array-buffer: 3.0.2 + dev: false + /array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-string: 1.0.7 + dev: false + /array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} dev: true @@ -5887,10 +5976,69 @@ packages: /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + dev: false + + /array.prototype.findlastindex@1.2.3: + resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 + dev: false + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: false + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: false + + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 + dev: false + + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: false /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + /ast-types-flow@0.0.7: + resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} + dev: false + /ast-types@0.15.2: resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} engines: {node: '>=4'} @@ -5916,6 +6064,12 @@ packages: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} dev: false + /asynciterator.prototype@1.0.0: + resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + dependencies: + has-symbols: 1.0.3 + dev: false + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -5924,6 +6078,16 @@ packages: engines: {node: '>= 4.0.0'} dev: false + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: false + + /axe-core@4.8.2: + resolution: {integrity: sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==} + engines: {node: '>=4'} + dev: false + /axios@0.26.1(debug@4.3.4): resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} dependencies: @@ -5942,6 +6106,12 @@ packages: - debug dev: false + /axobject-query@3.2.1: + resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + dependencies: + dequal: 2.0.3 + dev: false + /babel-core@7.0.0-bridge.0(@babel/core@7.23.2): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: @@ -6236,13 +6406,6 @@ packages: dependencies: stream-buffers: 2.2.0 - /bplist-parser@0.2.0: - resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} - engines: {node: '>= 5.10.0'} - dependencies: - big-integer: 1.6.51 - dev: true - /bplist-parser@0.3.1: resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} engines: {node: '>= 5.10.0'} @@ -6327,13 +6490,6 @@ packages: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} dev: false - /bundle-name@3.0.0: - resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} - engines: {node: '>=12'} - dependencies: - run-applescript: 5.0.0 - dev: true - /burnt@0.12.1(expo@49.0.16)(react-dom@18.2.0)(react-native@0.72.6)(react@18.2.0): resolution: {integrity: sha512-a5EQvG8CR1JDmOyY9aiUORnJ39wpmZMMva5qbCTXadtNJAjLDZTvrmp7cvYB9i+Jfe1wVhvCCFfEios7M9/88Q==} peerDependencies: @@ -7029,6 +7185,10 @@ packages: resolution: {integrity: sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==} dev: false + /damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + dev: false + /data-urls@3.0.2: resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} engines: {node: '>=12'} @@ -7121,24 +7281,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - /default-browser-id@3.0.0: - resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} - engines: {node: '>=12'} - dependencies: - bplist-parser: 0.2.0 - untildify: 4.0.0 - dev: true - - /default-browser@4.0.0: - resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} - engines: {node: '>=14.16'} - dependencies: - bundle-name: 3.0.0 - default-browser-id: 3.0.0 - execa: 7.2.0 - titleize: 3.0.0 - dev: true - /default-gateway@4.2.0: resolution: {integrity: sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==} engines: {node: '>=6'} @@ -7169,10 +7311,14 @@ packages: engines: {node: '>=8'} dev: false - /define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - dev: true + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + has-property-descriptors: 1.0.1 + object-keys: 1.1.1 + dev: false /del@6.1.1: resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==} @@ -7212,6 +7358,11 @@ packages: prop-types: 15.8.1 dev: false + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + /destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -7258,6 +7409,14 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 + dev: false + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: false /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -7437,10 +7596,98 @@ packages: escape-html: 1.0.3 dev: false + /es-abstract@1.22.3: + resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.2 + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + es-set-tostringtag: 2.0.2 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.2 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + internal-slot: 1.0.6 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.1 + safe-array-concat: 1.0.1 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.13 + dev: false + + /es-iterator-helpers@1.0.15: + resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + dependencies: + asynciterator.prototype: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-set-tostringtag: 2.0.2 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + iterator.prototype: 1.1.2 + safe-array-concat: 1.0.1 + dev: false + /es-module-lexer@1.3.1: resolution: {integrity: sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==} dev: true + /es-set-tostringtag@2.0.2: + resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + has-tostringtag: 1.0.0 + hasown: 2.0.0 + dev: false + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.0 + dev: false + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: false + /esbuild-register@3.5.0(esbuild@0.19.5): resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==} peerDependencies: @@ -7520,35 +7767,165 @@ packages: eslint: '>=7.0.0' dependencies: eslint: 8.52.0 - dev: true + dev: false + + /eslint-config-turbo@1.10.15(eslint@8.52.0): + resolution: {integrity: sha512-76mpx2x818JZE26euen14utYcFDxOahZ9NaWA+6Xa4pY2ezVKVschuOxS96EQz3o3ZRSmcgBOapw/gHbN+EKxQ==} + peerDependencies: + eslint: '>6.6.0' + dependencies: + eslint: 8.52.0 + eslint-plugin-turbo: 1.10.15(eslint@8.52.0) + dev: false + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: false - /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.52.0)(prettier@3.0.3): - resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} - engines: {node: ^14.18.0 || >=16.0.0} + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.8.0)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '*' - prettier: '>=3.0.0' + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' peerDependenciesMeta: - '@types/eslint': + '@typescript-eslint/parser': optional: true - eslint-config-prettier: + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: optional: true dependencies: + '@typescript-eslint/parser': 6.8.0(eslint@8.52.0)(typescript@5.2.2) + debug: 3.2.7 eslint: 8.52.0 - eslint-config-prettier: 9.0.0(eslint@8.52.0) - prettier: 3.0.3 - prettier-linter-helpers: 1.0.0 - synckit: 0.8.5 - dev: true + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: false - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.8.0)(eslint@8.52.0): + resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 + '@typescript-eslint/parser': 6.8.0(eslint@8.52.0)(typescript@5.2.2) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.52.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.8.0)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0) + has: 1.0.4 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: false + + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.52.0): + resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + '@babel/runtime': 7.23.2 + aria-query: 5.3.0 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + ast-types-flow: 0.0.7 + axe-core: 4.8.2 + axobject-query: 3.2.1 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 8.52.0 + has: 1.0.4 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + semver: 6.3.1 + dev: false + + /eslint-plugin-react-hooks@4.6.0(eslint@8.52.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.52.0 + dev: false + + /eslint-plugin-react@7.33.2(eslint@8.52.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.15 + eslint: 8.52.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 + dev: false + + /eslint-plugin-turbo@1.10.15(eslint@8.52.0): + resolution: {integrity: sha512-Tv4QSKV/U56qGcTqS/UgOvb9HcKFmWOQcVh3HEaj7of94lfaENgfrtK48E2CckQf7amhKs1i+imhCsNCKjkQyA==} + peerDependencies: + eslint: '>6.6.0' + dependencies: + dotenv: 16.0.3 + eslint: 8.52.0 + dev: false + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 dev: true /eslint-scope@7.2.2: @@ -7706,21 +8083,6 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - /execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.1.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - dev: true - /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -8259,10 +8621,6 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true - /fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} @@ -8272,6 +8630,7 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 + dev: false /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -8483,6 +8842,12 @@ packages: resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} dev: false + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: false + /foreachasync@3.0.0: resolution: {integrity: sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==} dev: false @@ -8674,6 +9039,20 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + functions-have-names: 1.2.3 + dev: false + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: false + /gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -8736,6 +9115,14 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: false + /get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} dependencies: @@ -8835,6 +9222,13 @@ packages: dependencies: type-fest: 0.20.2 + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: false + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -8845,6 +9239,7 @@ packages: ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 + dev: false /globby@13.2.2: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} @@ -8932,6 +9327,10 @@ packages: uglify-js: 3.17.4 dev: false + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: false + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -8958,10 +9357,22 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: false + /has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} dev: false + /has@1.0.4: + resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} + engines: {node: '>= 0.4.0'} + dev: false + /hasown@2.0.0: resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} engines: {node: '>= 0.4'} @@ -9098,11 +9509,6 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - /human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} - dev: true - /humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} dependencies: @@ -9258,6 +9664,15 @@ packages: ipaddr.js: 1.9.1 dev: false + /internal-slot@1.0.6: + resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + hasown: 2.0.0 + side-channel: 1.0.4 + dev: false + /interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} @@ -9286,6 +9701,14 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: false + /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -9293,21 +9716,54 @@ packages: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} dev: false + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: false + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: false + /is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} dev: false + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: false + /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.0 + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + /is-directory@0.3.1: resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==} engines: {node: '>=0.10.0'} @@ -9317,12 +9773,7 @@ packages: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true - - /is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - dev: true + dev: false /is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} @@ -9338,6 +9789,12 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.5 + dev: false + /is-fullwidth-code-point@2.0.0: resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} engines: {node: '>=4'} @@ -9352,6 +9809,13 @@ packages: engines: {node: '>=6'} dev: true + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + /is-glob@2.0.1: resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} engines: {node: '>=0.10.0'} @@ -9365,14 +9829,6 @@ packages: dependencies: is-extglob: 2.1.1 - /is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - dependencies: - is-docker: 3.0.0 - dev: true - /is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -9388,6 +9844,22 @@ packages: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: false + /is-map@2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: false + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: false + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -9427,6 +9899,24 @@ packages: engines: {node: '>=0.10.0'} dev: false + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: false + + /is-set@2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: false + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.5 + dev: false + /is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -9436,10 +9926,26 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: false + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.13 + dev: false /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} @@ -9452,6 +9958,23 @@ packages: is-invalid-path: 0.1.0 dev: false + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: false + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.5 + dev: false + + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: false + /is-whitespace@0.3.0: resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==} engines: {node: '>=0.10.0'} @@ -9467,10 +9990,15 @@ packages: engines: {node: '>=8'} dependencies: is-docker: 2.2.1 + dev: false /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: false + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -9546,6 +10074,16 @@ packages: resolution: {integrity: sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw==} dev: false + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.4 + set-function-name: 2.0.1 + dev: false + /jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} @@ -10235,6 +10773,13 @@ packages: /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: false + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -10283,6 +10828,16 @@ packages: semver: 7.5.4 dev: false + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 + object.assign: 4.1.4 + object.values: 1.1.7 + dev: false + /jwa@1.4.1: resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} dependencies: @@ -10325,6 +10880,16 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + /language-subtag-registry@0.3.22: + resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + dev: false + + /language-tags@1.0.5: + resolution: {integrity: sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==} + dependencies: + language-subtag-registry: 0.3.22 + dev: false + /leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} dev: false @@ -10724,6 +11289,7 @@ packages: /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + dev: false /methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} @@ -11285,11 +11851,6 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true - /mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -11660,13 +12221,6 @@ packages: dependencies: path-key: 3.1.1 - /npm-run-path@5.1.0: - resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - path-key: 4.0.0 - dev: true - /npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -11708,6 +12262,64 @@ packages: /object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: false + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: false + + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /object.groupby@1.0.1: + resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + dev: false + + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + /on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} @@ -11744,13 +12356,6 @@ packages: dependencies: mimic-fn: 2.1.0 - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - dependencies: - mimic-fn: 4.0.0 - dev: true - /open@6.4.0: resolution: {integrity: sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==} engines: {node: '>=8'} @@ -11767,16 +12372,6 @@ packages: is-wsl: 2.2.0 dev: false - /open@9.1.0: - resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} - engines: {node: '>=14.16'} - dependencies: - default-browser: 4.0.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 2.2.0 - dev: true - /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -11987,11 +12582,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true - /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -12115,17 +12705,11 @@ packages: engines: {node: '>=4'} dev: false - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - dependencies: - fast-diff: 1.3.0 - dev: true - /prettier@3.0.3: resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} engines: {node: '>=14'} hasBin: true + dev: false /pretty-bytes@5.6.0: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} @@ -12763,6 +13347,18 @@ packages: /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /reflect.getprototypeof@1.0.4: + resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: false + /reforest@0.13.0(@types/react@18.2.31)(react@18.2.0): resolution: {integrity: sha512-f0It/s51f1UWCCCni0viULALDBhxWBPFnLmZRYtKcz4zYeNWqeNTdcnU/OpBry9tk+jyMQcH3MLK8UdzsAvA5w==} peerDependencies: @@ -12801,6 +13397,15 @@ packages: '@babel/runtime': 7.23.2 dev: false + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + set-function-name: 2.0.1 + dev: false + /regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -12922,6 +13527,15 @@ packages: path-parse: 1.0.7 dev: false + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + /responselike@1.0.2: resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} dependencies: @@ -12992,13 +13606,6 @@ packages: resolution: {integrity: sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==} dev: false - /run-applescript@5.0.0: - resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} - engines: {node: '>=12'} - dependencies: - execa: 5.1.1 - dev: true - /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -13014,6 +13621,16 @@ packages: dependencies: tslib: 2.6.2 + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: false + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -13026,6 +13643,14 @@ packages: dev: false optional: true + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-regex: 1.1.4 + dev: false + /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -13177,6 +13802,15 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.1 + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.1 + dev: false + /set-value@4.1.0: resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==} engines: {node: '>=11.0'} @@ -13493,6 +14127,45 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + regexp.prototype.flags: 1.5.1 + set-function-name: 2.0.1 + side-channel: 1.0.4 + dev: false + + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: false + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -13540,11 +14213,6 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true - /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -13704,14 +14372,6 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true - /synckit@0.8.5: - resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} - engines: {node: ^14.18.0 || >=16.0.0} - dependencies: - '@pkgr/utils': 2.4.2 - tslib: 2.6.2 - dev: true - /tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} dev: false @@ -13930,11 +14590,6 @@ packages: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} dev: false - /titleize@3.0.0: - resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} - engines: {node: '>=12'} - dev: true - /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -13999,7 +14654,7 @@ packages: typescript: '>=4.2.0' dependencies: typescript: 5.2.2 - dev: true + dev: false /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -14094,6 +14749,15 @@ packages: tsconfig-paths: 4.2.0 dev: true + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: false + /tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -14237,6 +14901,44 @@ packages: media-typer: 0.3.0 mime-types: 2.1.35 + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: false + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: false + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: false + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: false + /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} @@ -14273,6 +14975,15 @@ packages: dependencies: '@lukeed/csprng': 1.1.0 + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.5 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: false + /undici-types@5.25.3: resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} @@ -14362,11 +15073,6 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - dev: true - /update-browserslist-db@1.0.13(browserslist@4.22.1): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -14656,10 +15362,58 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: false + + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.0 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.13 + dev: false + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: false + /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: false + /which-typed-array@1.1.13: + resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: false + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true diff --git a/tsconfig.base.json b/tsconfig.base.json deleted file mode 100644 index 9810231..0000000 --- a/tsconfig.base.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": ".", - "rootDir": ".", - "importHelpers": true, - "allowJs": false, - "allowSyntheticDefaultImports": true, - "downlevelIteration": true, - "esModuleInterop": true, - "preserveSymlinks": true, - "incremental": true, - "jsx": "react-jsx", - "module": "system", - "moduleResolution": "node", - "noEmitOnError": false, - "noImplicitAny": false, - "noImplicitReturns": false, - "noUnusedLocals": false, - "noUnusedParameters": false, - "useUnknownInCatchVariables": false, - "preserveConstEnums": true, - // DONT DO THIS so jsdoc will remain - "removeComments": false, - "skipLibCheck": true, - "sourceMap": false, - "strictNullChecks": true, - "target": "es2020", - "types": ["node"], - "lib": ["dom", "esnext"] - }, - "exclude": ["_"], - "typeAcquisition": { - "enable": true - } -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 865b9ad..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "strictNullChecks": true, - "noUncheckedIndexedAccess": true, - "paths": { - "app/*": ["./packages/app/*"], - "@my/ui/*": ["./packages/ui/*"] - } - }, - "extends": "expo/tsconfig.base", - "exclude": ["**/node_modules", "**/dist", "**/types", "apps/next/out", "apps/next/.next", "apps/next/.tamagui"] -}