From fdb3768d6319989c587a8581b875245dff544392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=5BQuy=20Anh=5D=20=C2=ABElliot=C2=BB=20Nguyen?= Date: Wed, 24 Jun 2026 14:10:53 +0200 Subject: [PATCH] --- --- .dockerignore | 2 + .editorconfig | 9 + .env.example | 32 + .gitignore | 5 + Dockerfile | 15 + README.md | 1 + env.d.ts | 42 + package-lock.json | 6944 +++++++++++++++++ package.json | 62 + src/collections/Conversation.ts | 35 + src/collections/Inbox.ts | 17 + src/collections/index.ts | 2 + src/configs/aws.config.ts | 12 + src/configs/firebase.config.ts | 14 + src/configs/index.ts | 3 + src/configs/type-orm.config.ts | 39 + src/constants.ts | 4 + src/entities/CV.ts | 30 + src/entities/Comment.ts | 60 + src/entities/CommentVote.ts | 27 + src/entities/Conversation.ts | 18 + src/entities/EducationItem.ts | 42 + src/entities/Experience.ts | 42 + src/entities/Offer.ts | 104 + src/entities/OfferApplication.ts | 31 + src/entities/Page.ts | 84 + src/entities/PageFollow.ts | 24 + src/entities/Post.ts | 73 + src/entities/Qualification.ts | 54 + src/entities/Space.ts | 73 + src/entities/SpaceSubscription.ts | 23 + src/entities/Tag.ts | 14 + src/entities/User.ts | 124 + src/entities/UserFollow.ts | 23 + src/entities/Vote.ts | 27 + src/entities/index.ts | 18 + .../CreateConversationReponse.ts | 12 + src/graphql-types/EducationItemInput.ts | 20 + src/graphql-types/EducationItemResponse.ts | 12 + src/graphql-types/ExperienceInput.ts | 20 + src/graphql-types/ExperienceResponse.ts | 12 + src/graphql-types/FieldError.ts | 10 + src/graphql-types/ForgotPasswordResponse.ts | 11 + src/graphql-types/InboxResponse.ts | 11 + src/graphql-types/Location.ts | 10 + src/graphql-types/MessageInput.ts | 14 + src/graphql-types/OfferInput.ts | 80 + src/graphql-types/OfferResponse.ts | 12 + src/graphql-types/PageInfo.ts | 16 + src/graphql-types/PageResponse.ts | 12 + src/graphql-types/PaginatedComments.ts | 11 + src/graphql-types/PaginatedOffers.ts | 11 + src/graphql-types/PaginatedPosts.ts | 11 + src/graphql-types/PhotoResponse.ts | 10 + src/graphql-types/PostInput.ts | 31 + src/graphql-types/PostResponse.ts | 12 + src/graphql-types/QualificationInput.ts | 29 + src/graphql-types/QualificationResponse.ts | 12 + src/graphql-types/SpaceInfo.ts | 16 + src/graphql-types/SpaceResponse.ts | 12 + src/graphql-types/UploadedFileResponse.ts | 16 + src/graphql-types/UserInfo.ts | 16 + src/graphql-types/UserResponse.ts | 12 + src/graphql-types/index.ts | 27 + src/index.ts | 140 + src/loaders/createApplicantsNoLoader.ts | 20 + src/loaders/createApplicationLoader.ts | 11 + src/loaders/createApplicationStatusLoader.ts | 15 + src/loaders/createCommentVoteLoader.ts | 18 + src/loaders/createFollowLoader.ts | 16 + src/loaders/createFollowerNumberLoader.ts | 20 + src/loaders/createModStatusLoader.ts | 16 + src/loaders/createOwnerStatusLoader.ts | 15 + src/loaders/createPageLoader.ts | 15 + src/loaders/createSpaceLoader.ts | 15 + src/loaders/createSubscriberNumberLoader.ts | 20 + src/loaders/createSubscriptionLoader.ts | 16 + src/loaders/createTagLoader.ts | 27 + src/loaders/createUserFollowLoader.ts | 16 + src/loaders/createUserFollowerNumberLoader.ts | 20 + .../createUserFollowingNumberLoader.ts | 20 + src/loaders/createUserLoader.ts | 15 + src/loaders/createVoteLoader.ts | 18 + src/loaders/index.ts | 18 + src/middlewares/index.ts | 1 + src/middlewares/isAuth.ts | 12 + src/migrations/1660494342130-Initial.ts | 128 + src/migrations/1660494342131-AddSpaces.ts | 39 + src/migrations/1660494342132-AddUsers.ts | 135 + .../1660494342133-AddAdminAsModToSpaces.ts | 34 + src/migrations/1660494342134-AddPages.ts | 337 + .../1660494342135-AddSpaceSubscribers.ts | 29 + src/migrations/1660494342136-AddTags.ts | 33 + src/migrations/1660494342137-AddPosts.ts | 43 + src/migrations/1660494342138-AddOffers.ts | 67 + src/resolvers/comment.ts | 202 + src/resolvers/conversation.ts | 215 + src/resolvers/cv.ts | 67 + src/resolvers/educationItem.ts | 118 + src/resolvers/experience.ts | 118 + src/resolvers/index.ts | 14 + src/resolvers/offer.ts | 431 + src/resolvers/page.ts | 360 + src/resolvers/post.ts | 333 + src/resolvers/qualification.ts | 137 + src/resolvers/quicksight.ts | 20 + src/resolvers/s3.ts | 20 + src/resolvers/space.ts | 391 + src/resolvers/tag.ts | 10 + src/resolvers/user.ts | 542 ++ src/type-orm.source.ts | 4 + src/types.ts | 49 + src/utils/getExtensionFromFilename.ts | 3 + src/utils/index.ts | 8 + src/utils/isValidEmail.ts | 2 + src/utils/saveAttributes.ts | 9 + src/utils/sendEmail.ts | 24 + src/utils/sleep.ts | 1 + src/utils/toPostgresTime.ts | 4 + src/utils/validateLogin.ts | 59 + src/utils/validateRegister.ts | 43 + tsconfig.json | 119 + 122 files changed, 13239 insertions(+) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 env.d.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/collections/Conversation.ts create mode 100644 src/collections/Inbox.ts create mode 100644 src/collections/index.ts create mode 100644 src/configs/aws.config.ts create mode 100644 src/configs/firebase.config.ts create mode 100644 src/configs/index.ts create mode 100644 src/configs/type-orm.config.ts create mode 100644 src/constants.ts create mode 100644 src/entities/CV.ts create mode 100644 src/entities/Comment.ts create mode 100644 src/entities/CommentVote.ts create mode 100644 src/entities/Conversation.ts create mode 100644 src/entities/EducationItem.ts create mode 100644 src/entities/Experience.ts create mode 100644 src/entities/Offer.ts create mode 100644 src/entities/OfferApplication.ts create mode 100644 src/entities/Page.ts create mode 100644 src/entities/PageFollow.ts create mode 100644 src/entities/Post.ts create mode 100644 src/entities/Qualification.ts create mode 100644 src/entities/Space.ts create mode 100644 src/entities/SpaceSubscription.ts create mode 100644 src/entities/Tag.ts create mode 100644 src/entities/User.ts create mode 100644 src/entities/UserFollow.ts create mode 100644 src/entities/Vote.ts create mode 100644 src/entities/index.ts create mode 100644 src/graphql-types/CreateConversationReponse.ts create mode 100644 src/graphql-types/EducationItemInput.ts create mode 100644 src/graphql-types/EducationItemResponse.ts create mode 100644 src/graphql-types/ExperienceInput.ts create mode 100644 src/graphql-types/ExperienceResponse.ts create mode 100644 src/graphql-types/FieldError.ts create mode 100644 src/graphql-types/ForgotPasswordResponse.ts create mode 100644 src/graphql-types/InboxResponse.ts create mode 100644 src/graphql-types/Location.ts create mode 100644 src/graphql-types/MessageInput.ts create mode 100644 src/graphql-types/OfferInput.ts create mode 100644 src/graphql-types/OfferResponse.ts create mode 100644 src/graphql-types/PageInfo.ts create mode 100644 src/graphql-types/PageResponse.ts create mode 100644 src/graphql-types/PaginatedComments.ts create mode 100644 src/graphql-types/PaginatedOffers.ts create mode 100644 src/graphql-types/PaginatedPosts.ts create mode 100644 src/graphql-types/PhotoResponse.ts create mode 100644 src/graphql-types/PostInput.ts create mode 100644 src/graphql-types/PostResponse.ts create mode 100644 src/graphql-types/QualificationInput.ts create mode 100644 src/graphql-types/QualificationResponse.ts create mode 100644 src/graphql-types/SpaceInfo.ts create mode 100644 src/graphql-types/SpaceResponse.ts create mode 100644 src/graphql-types/UploadedFileResponse.ts create mode 100644 src/graphql-types/UserInfo.ts create mode 100644 src/graphql-types/UserResponse.ts create mode 100644 src/graphql-types/index.ts create mode 100644 src/index.ts create mode 100644 src/loaders/createApplicantsNoLoader.ts create mode 100644 src/loaders/createApplicationLoader.ts create mode 100644 src/loaders/createApplicationStatusLoader.ts create mode 100644 src/loaders/createCommentVoteLoader.ts create mode 100644 src/loaders/createFollowLoader.ts create mode 100644 src/loaders/createFollowerNumberLoader.ts create mode 100644 src/loaders/createModStatusLoader.ts create mode 100644 src/loaders/createOwnerStatusLoader.ts create mode 100644 src/loaders/createPageLoader.ts create mode 100644 src/loaders/createSpaceLoader.ts create mode 100644 src/loaders/createSubscriberNumberLoader.ts create mode 100644 src/loaders/createSubscriptionLoader.ts create mode 100644 src/loaders/createTagLoader.ts create mode 100644 src/loaders/createUserFollowLoader.ts create mode 100644 src/loaders/createUserFollowerNumberLoader.ts create mode 100644 src/loaders/createUserFollowingNumberLoader.ts create mode 100644 src/loaders/createUserLoader.ts create mode 100644 src/loaders/createVoteLoader.ts create mode 100644 src/loaders/index.ts create mode 100644 src/middlewares/index.ts create mode 100644 src/middlewares/isAuth.ts create mode 100644 src/migrations/1660494342130-Initial.ts create mode 100644 src/migrations/1660494342131-AddSpaces.ts create mode 100644 src/migrations/1660494342132-AddUsers.ts create mode 100644 src/migrations/1660494342133-AddAdminAsModToSpaces.ts create mode 100644 src/migrations/1660494342134-AddPages.ts create mode 100644 src/migrations/1660494342135-AddSpaceSubscribers.ts create mode 100644 src/migrations/1660494342136-AddTags.ts create mode 100644 src/migrations/1660494342137-AddPosts.ts create mode 100644 src/migrations/1660494342138-AddOffers.ts create mode 100644 src/resolvers/comment.ts create mode 100644 src/resolvers/conversation.ts create mode 100644 src/resolvers/cv.ts create mode 100644 src/resolvers/educationItem.ts create mode 100644 src/resolvers/experience.ts create mode 100644 src/resolvers/index.ts create mode 100644 src/resolvers/offer.ts create mode 100644 src/resolvers/page.ts create mode 100644 src/resolvers/post.ts create mode 100644 src/resolvers/qualification.ts create mode 100644 src/resolvers/quicksight.ts create mode 100644 src/resolvers/s3.ts create mode 100644 src/resolvers/space.ts create mode 100644 src/resolvers/tag.ts create mode 100644 src/resolvers/user.ts create mode 100644 src/type-orm.source.ts create mode 100644 src/types.ts create mode 100644 src/utils/getExtensionFromFilename.ts create mode 100644 src/utils/index.ts create mode 100644 src/utils/isValidEmail.ts create mode 100644 src/utils/saveAttributes.ts create mode 100644 src/utils/sendEmail.ts create mode 100644 src/utils/sleep.ts create mode 100644 src/utils/toPostgresTime.ts create mode 100644 src/utils/validateLogin.ts create mode 100644 src/utils/validateRegister.ts create mode 100644 tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4cebe44 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +deploy.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0a1a13a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true +[*] +quote_type = single +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ef722f7 --- /dev/null +++ b/.env.example @@ -0,0 +1,32 @@ +DB_USERNAME= +DB_PASSWORD= +DB_HOST= +DB_PORT= +DB_DATABASE= +REDIS_URL= +REDIS_SECRET= +API_PORT= +FRONTEND_ORIGIN= +BACKEND_ORIGIN= +DOMAIN_SUFFIX= +NON_API_ERROR_PREFIX= +NON_API_ERROR_SUFFIX= +AWS_REGION= +AWS_ACCOUNT_ID= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +S3_BUCKET= +QUICKSIGHT_DASHBOARD_ID= +FIREBASE_ADMIN_TYPE= +FIREBASE_ADMIN_PROJECT_ID= +FIREBASE_ADMIN_PRIVATE_KEY_ID= +FIREBASE_ADMIN_PRIVATE_KEY= +FIREBASE_ADMIN_CLIENT_EMAIL= +FIREBASE_ADMIN_CLIENT_ID= +FIREBASE_ADMIN_AUTH_URI= +FIREBASE_TOKEN_URI= +FIREBASE_ADMIN_AUTH_PROVIDER_X509_CERT_URL= +FIREBASE_ADMIN_CLIENT_X509_CERT_URL= +SMTP_HOST= +SMTP_PORT= +SMTP_SECURE= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57272cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +.env +.env.development +.env.production +deploy.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6d0a376 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM node:latest + +WORKDIR /usr/src/app + +COPY package.json . +COPY package-lock.json . +RUN npm install + +COPY . . +COPY .env.production .env +ENV NODE_ENV production + +EXPOSE 4000 + +CMD ["npm", "run", "start"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..05e1fec --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Backend for Comroots diff --git a/env.d.ts b/env.d.ts new file mode 100644 index 0000000..354ae51 --- /dev/null +++ b/env.d.ts @@ -0,0 +1,42 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + DB_USERNAME: string; + DB_PASSWORD: string; + DB_HOST: string; + DB_PORT: string; + DB_DATABASE: string; + REDIS_URL: string; + REDIS_SECRET: string; + API_PORT: string; + FRONTEND_ORIGIN: string; + BACKEND_ORIGIN: string; + DOMAIN_SUFFIX: string; + NON_API_ERROR_PREFIX: string; + NON_API_ERROR_SUFFIX: string; + AWS_REGION: string; + AWS_ACCOUNT_ID: string; + AWS_ACCESS_KEY_ID: string; + AWS_SECRET_ACCESS_KEY: string; + S3_BUCKET: string; + QUICKSIGHT_DASHBOARD_ID: string; + FIREBASE_ADMIN_TYPE: string; + FIREBASE_ADMIN_PROJECT_ID: string; + FIREBASE_ADMIN_PRIVATE_KEY_ID: string; + FIREBASE_ADMIN_PRIVATE_KEY: string; + FIREBASE_ADMIN_CLIENT_EMAIL: string; + FIREBASE_ADMIN_CLIENT_ID: string; + FIREBASE_ADMIN_AUTH_URI: string; + FIREBASE_TOKEN_URI: string; + FIREBASE_ADMIN_AUTH_PROVIDER_X509_CERT_URL: string; + FIREBASE_ADMIN_CLIENT_X509_CERT_URL: string; + SMTP_HOST: string; + SMTP_PORT: string; + SMTP_SECURE: string; + SMTP_USER: string; + SMTP_PASS: string; + } + } +} + +export {} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1bf9754 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6944 @@ +{ + "name": "comroots-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "comroots-backend", + "version": "1.0.0", + "dependencies": { + "@faker-js/faker": "^7.4.0", + "@swc/core": "^1.2.213", + "@swc/helpers": "^0.4.3", + "@types/graphql-upload": "^8.0.11", + "apollo-server-core": "^3.8.2", + "apollo-server-errors": "^3.3.1", + "apollo-server-express": "^3.8.2", + "argon2": "^0.28.5", + "aws-sdk": "^2.1172.0", + "connect-redis": "^6.1.3", + "dataloader": "^2.1.0", + "dotenv-safe": "^8.2.0", + "express": "^4.18.1", + "express-session": "^1.17.3", + "firebase-admin": "^11.0.0", + "fireorm": "^0.23.3", + "graphql": "^15.3.0", + "graphql-upload": "13.0.0", + "ioredis": "^5.1.0", + "lodash": "^4.17.21", + "nodemailer": "^6.7.5", + "pg": "^8.7.3", + "redis-session": "^0.1.0", + "reflect-metadata": "^0.1.13", + "regenerator-runtime": "^0.13.9", + "ts-node": "^10.8.2", + "tsconfig-paths": "^4.0.0", + "type-graphql": "^1.1.1", + "typeorm": "^0.3.7", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@types/connect-redis": "^0.0.18", + "@types/dotenv-safe": "^8.1.2", + "@types/express": "^4.17.13", + "@types/express-session": "^1.17.4", + "@types/ioredis": "^4.28.10", + "@types/lodash": "^4.14.182", + "@types/node": "^17.0.41", + "@types/nodemailer": "^6.4.4", + "@types/uuid": "^8.3.4", + "gen-env-types": "^1.3.4", + "nodemon": "^2.0.16", + "typescript": "^4.7.3" + } + }, + "node_modules/@apollo/protobufjs": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz", + "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "long": "^4.0.0" + }, + "bin": { + "apollo-pbjs": "bin/pbjs", + "apollo-pbts": "bin/pbts" + } + }, + "node_modules/@apollo/usage-reporting-protobuf": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz", + "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==", + "license": "MIT", + "dependencies": { + "@apollo/protobufjs": "1.2.7" + } + }, + "node_modules/@apollo/utils.dropunuseddefinitions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz", + "integrity": "sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.keyvaluecache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz", + "integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==", + "license": "MIT", + "dependencies": { + "@apollo/utils.logger": "^1.0.0", + "lru-cache": "7.10.1 - 7.13.1" + } + }, + "node_modules/@apollo/utils.keyvaluecache/node_modules/lru-cache": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", + "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@apollo/utils.logger": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz", + "integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==", + "license": "MIT" + }, + "node_modules/@apollo/utils.printwithreducedwhitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz", + "integrity": "sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.removealiases": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz", + "integrity": "sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.sortast": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz", + "integrity": "sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA==", + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.stripsensitiveliterals": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz", + "integrity": "sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollo/utils.usagereporting": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz", + "integrity": "sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ==", + "license": "MIT", + "dependencies": { + "@apollo/usage-reporting-protobuf": "^4.0.0", + "@apollo/utils.dropunuseddefinitions": "^1.1.0", + "@apollo/utils.printwithreducedwhitespace": "^1.1.0", + "@apollo/utils.removealiases": "1.0.0", + "@apollo/utils.sortast": "^1.1.0", + "@apollo/utils.stripsensitiveliterals": "^1.2.0" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "graphql": "14.x || 15.x || 16.x" + } + }, + "node_modules/@apollographql/apollo-tools": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz", + "integrity": "sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==", + "license": "MIT", + "engines": { + "node": ">=8", + "npm": ">=6" + }, + "peerDependencies": { + "graphql": "^14.2.1 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/@apollographql/graphql-playground-html": { + "version": "1.6.29", + "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz", + "integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==", + "license": "MIT", + "dependencies": { + "xss": "^1.0.8" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@faker-js/faker": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz", + "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", + "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", + "license": "MIT", + "dependencies": { + "text-decoding": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/component": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.14.4.tgz", + "integrity": "sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz", + "integrity": "sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/database": "0.14.4", + "@firebase/database-types": "0.10.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz", + "integrity": "sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.0", + "@firebase/util": "1.9.3" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.8.0.tgz", + "integrity": "sha512-JRpk06SmZXLGz0pNx1x7yU3YhkUXheKgH5hbDZ4kMsdhtfV5qPLJLRI4wv69K0cZorIk+zTMOwptue7hizo0eA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^3.5.7", + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", + "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", + "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", + "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@google-cloud/storage": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.12.0.tgz", + "integrity": "sha512-78nNAY7iiZ4O/BouWMWTD/oSF2YtYgYB3GZirn0To6eBOugjXVoK+GXgUXOl+HlqbAOyHxAVXOlsj3snfbQ1dw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^3.0.7", + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^3.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "compressible": "^2.0.12", + "duplexify": "^4.0.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "fast-xml-parser": "^4.2.2", + "gaxios": "^5.0.0", + "google-auth-library": "^8.0.1", + "mime": "^3.0.0", + "mime-types": "^2.0.8", + "p-limit": "^3.0.1", + "retry-request": "^5.0.0", + "teeny-request": "^8.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@graphql-tools/merge": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz", + "integrity": "sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", + "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/mock": { + "version": "8.7.20", + "resolved": "https://registry.npmjs.org/@graphql-tools/mock/-/mock-8.7.20.tgz", + "integrity": "sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ==", + "license": "MIT", + "dependencies": { + "@graphql-tools/schema": "^9.0.18", + "@graphql-tools/utils": "^9.2.1", + "fast-json-stable-stringify": "^2.1.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/mock/node_modules/@graphql-tools/merge": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz", + "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^9.2.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/mock/node_modules/@graphql-tools/schema": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", + "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", + "license": "MIT", + "dependencies": { + "@graphql-tools/merge": "^8.4.1", + "@graphql-tools/utils": "^9.2.1", + "tslib": "^2.4.0", + "value-or-promise": "^1.0.12" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/mock/node_modules/value-or-promise": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", + "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@graphql-tools/schema": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz", + "integrity": "sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==", + "license": "MIT", + "dependencies": { + "@graphql-tools/merge": "8.3.1", + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", + "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/utils": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", + "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.8.22", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.22.tgz", + "integrity": "sha512-oAjDdN7fzbUi+4hZjKG96MR6KTEubAeMpQEb+77qy+3r0Ua5xTFuie6JOLr4ZZgl5g+W5/uRTS2M1V8mVAFPuA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@josephg/resolvable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", + "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==", + "license": "ISC" + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.8.tgz", + "integrity": "sha512-5e+SFVavj1ORKlKaKr2BmTOekmXbelU7dC0cDkQLqag7xfuTPuGMUFx7KWJuv4bYZrTsoL2Z18VVCOKYxzoHcg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", + "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", + "license": "MIT" + }, + "node_modules/@swc/core": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.26.tgz", + "integrity": "sha512-f5uYFf+TmMQyYIoxkn/evWhNGuUzC730dFwAKGwBVHHVoPyak1/GvJUm6i1SKl+2Hrj9oN0i3WSoWWZ4pgI8lw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.7.26", + "@swc/core-darwin-x64": "1.7.26", + "@swc/core-linux-arm-gnueabihf": "1.7.26", + "@swc/core-linux-arm64-gnu": "1.7.26", + "@swc/core-linux-arm64-musl": "1.7.26", + "@swc/core-linux-x64-gnu": "1.7.26", + "@swc/core-linux-x64-musl": "1.7.26", + "@swc/core-win32-arm64-msvc": "1.7.26", + "@swc/core-win32-ia32-msvc": "1.7.26", + "@swc/core-win32-x64-msvc": "1.7.26" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.26.tgz", + "integrity": "sha512-FF3CRYTg6a7ZVW4yT9mesxoVVZTrcSWtmZhxKCYJX9brH4CS/7PRPjAKNk6kzWgWuRoglP7hkjQcd6EpMcZEAw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.26.tgz", + "integrity": "sha512-az3cibZdsay2HNKmc4bjf62QVukuiMRh5sfM5kHR/JMTrLyS6vSw7Ihs3UTkZjUxkLTT8ro54LI6sV6sUQUbLQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.26.tgz", + "integrity": "sha512-VYPFVJDO5zT5U3RpCdHE5v1gz4mmR8BfHecUZTmD2v1JeFY6fv9KArJUpjrHEEsjK/ucXkQFmJ0jaiWXmpOV9Q==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.26.tgz", + "integrity": "sha512-YKevOV7abpjcAzXrhsl+W48Z9mZvgoVs2eP5nY+uoMAdP2b3GxC0Df1Co0I90o2lkzO4jYBpTMcZlmUXLdXn+Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.26.tgz", + "integrity": "sha512-3w8iZICMkQQON0uIcvz7+Q1MPOW6hJ4O5ETjA0LSP/tuKqx30hIniCGOgPDnv3UTMruLUnQbtBwVCZTBKR3Rkg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.26.tgz", + "integrity": "sha512-c+pp9Zkk2lqb06bNGkR2Looxrs7FtGDMA4/aHjZcCqATgp348hOKH5WPvNLBl+yPrISuWjbKDVn3NgAvfvpH4w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.26.tgz", + "integrity": "sha512-PgtyfHBF6xG87dUSSdTJHwZ3/8vWZfNIXQV2GlwEpslrOkGqy+WaiiyE7Of7z9AvDILfBBBcJvJ/r8u980wAfQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.26.tgz", + "integrity": "sha512-9TNXPIJqFynlAOrRD6tUQjMq7KApSklK3R/tXgIxc7Qx+lWu8hlDQ/kVPLpU7PWvMMwC/3hKBW+p5f+Tms1hmA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.26.tgz", + "integrity": "sha512-9YngxNcG3177GYdsTum4V98Re+TlCeJEP4kEwEg9EagT5s3YejYdKwVAkAsJszzkXuyRDdnHUpYbTrPG6FiXrQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.26.tgz", + "integrity": "sha512-VR+hzg9XqucgLjXxA13MtV5O3C0bK0ywtLIBw/+a+O+Oc6mxFWHtdUeXDbIi5AiPbn0fjgVJMqYnyjGyyX8u0w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.4.36", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.36.tgz", + "integrity": "sha512-5lxnyLEYFskErRPenYItLRSge5DjrJngYKdVjRSrWfza9G6KkgHEXi0vUZiyUeMU5JfXH1YnvXZzSp8ul88o2Q==", + "license": "Apache-2.0", + "dependencies": { + "legacy-swc-helpers": "npm:@swc/helpers@=0.4.14", + "tslib": "^2.4.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, + "node_modules/@types/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-redis": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@types/connect-redis/-/connect-redis-0.0.18.tgz", + "integrity": "sha512-iGygGbXgPIr94DEAuoluWhzre3c2/ew5NPlbW9IWvwCTXMM1YCmc7M9wpXMkYqt6kB9aO1sjZnmDzyugUu+2vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/express-session": "*", + "@types/ioredis": "*", + "@types/redis": "^2.8.0" + } + }, + "node_modules/@types/content-disposition": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.8.tgz", + "integrity": "sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==", + "license": "MIT" + }, + "node_modules/@types/cookies": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.0.tgz", + "integrity": "sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "license": "MIT" + }, + "node_modules/@types/dotenv-safe": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@types/dotenv-safe/-/dotenv-safe-8.1.6.tgz", + "integrity": "sha512-ftZXu3WGT6ALq+f98IX2gWriGMPds+0ku5h8kZewNpY47ua+Z+XNcin9apZ2kVd4B9LV1vMfUOyDf1/hhreR0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "dotenv": "^8.2.0" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-27JdDRgor6PoYlURY+Y5kCakqp5ulC0kmf7y+QwaY+hv9jEFuQOThgkjyA53RP3jmKuBsH5GR6qEfFmvb8mwOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, + "node_modules/@types/graphql-upload": { + "version": "8.0.12", + "resolved": "https://registry.npmjs.org/@types/graphql-upload/-/graphql-upload-8.0.12.tgz", + "integrity": "sha512-M0ZPZqNUzKNB16q5woEzgG/Q8DjICV80K7JvDSRnDmDFfrRdfFX/n6PbmqAN7gCzECcHVnw1gk6N4Cg0FwxCqA==", + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/koa": "*", + "fs-capacitor": "^8.0.0", + "graphql": "0.13.1 - 16" + } + }, + "node_modules/@types/http-assert": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz", + "integrity": "sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "license": "MIT" + }, + "node_modules/@types/ioredis": { + "version": "4.28.10", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", + "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/keygrip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", + "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", + "license": "MIT" + }, + "node_modules/@types/koa": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.0.tgz", + "integrity": "sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==", + "license": "MIT", + "dependencies": { + "@types/accepts": "*", + "@types/content-disposition": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/http-errors": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "node_modules/@types/koa-compose": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.8.tgz", + "integrity": "sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==", + "license": "MIT", + "dependencies": { + "@types/koa": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, + "node_modules/@types/nodemailer": { + "version": "6.4.16", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.16.tgz", + "integrity": "sha512-uz6hN6Pp0upXMcilM61CoKyjT7sskBoOWpptkjjJp8jIMlTdc3xG01U7proKkXzruMS4hS0zqtHNkNPFB20rKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/redis": { + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.32.tgz", + "integrity": "sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.12.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", + "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==", + "license": "MIT", + "peer": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "optional": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/apollo-datasource": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.2.tgz", + "integrity": "sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg==", + "deprecated": "The `apollo-datasource` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", + "license": "MIT", + "dependencies": { + "@apollo/utils.keyvaluecache": "^1.0.1", + "apollo-server-env": "^4.2.1" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/apollo-reporting-protobuf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz", + "integrity": "sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog==", + "deprecated": "The `apollo-reporting-protobuf` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/usage-reporting-protobuf` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", + "license": "MIT", + "dependencies": { + "@apollo/protobufjs": "1.2.6" + } + }, + "node_modules/apollo-reporting-protobuf/node_modules/@apollo/protobufjs": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.6.tgz", + "integrity": "sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + }, + "bin": { + "apollo-pbjs": "bin/pbjs", + "apollo-pbts": "bin/pbts" + } + }, + "node_modules/apollo-reporting-protobuf/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "license": "MIT" + }, + "node_modules/apollo-server-core": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.13.0.tgz", + "integrity": "sha512-v/g6DR6KuHn9DYSdtQijz8dLOkP78I5JSVJzPkARhDbhpH74QNwrQ2PP2URAPPEDJ2EeZNQDX8PvbYkAKqg+kg==", + "license": "MIT", + "dependencies": { + "@apollo/utils.keyvaluecache": "^1.0.1", + "@apollo/utils.logger": "^1.0.0", + "@apollo/utils.usagereporting": "^1.0.0", + "@apollographql/apollo-tools": "^0.5.3", + "@apollographql/graphql-playground-html": "1.6.29", + "@graphql-tools/mock": "^8.1.2", + "@graphql-tools/schema": "^8.0.0", + "@josephg/resolvable": "^1.0.0", + "apollo-datasource": "^3.3.2", + "apollo-reporting-protobuf": "^3.4.0", + "apollo-server-env": "^4.2.1", + "apollo-server-errors": "^3.3.1", + "apollo-server-plugin-base": "^3.7.2", + "apollo-server-types": "^3.8.0", + "async-retry": "^1.2.1", + "fast-json-stable-stringify": "^2.1.0", + "graphql-tag": "^2.11.0", + "loglevel": "^1.6.8", + "lru-cache": "^6.0.0", + "node-abort-controller": "^3.0.1", + "sha.js": "^2.4.11", + "uuid": "^9.0.0", + "whatwg-mimetype": "^3.0.0" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "graphql": "^15.3.0 || ^16.0.0" + } + }, + "node_modules/apollo-server-core/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/apollo-server-env": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-4.2.1.tgz", + "integrity": "sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g==", + "deprecated": "The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/apollo-server-errors": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz", + "integrity": "sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA==", + "deprecated": "The `apollo-server-errors` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "graphql": "^15.3.0 || ^16.0.0" + } + }, + "node_modules/apollo-server-express": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.13.0.tgz", + "integrity": "sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g==", + "license": "MIT", + "dependencies": { + "@types/accepts": "^1.3.5", + "@types/body-parser": "1.19.2", + "@types/cors": "2.8.12", + "@types/express": "4.17.14", + "@types/express-serve-static-core": "4.17.31", + "accepts": "^1.3.5", + "apollo-server-core": "^3.13.0", + "apollo-server-types": "^3.8.0", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "parseurl": "^1.3.3" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "express": "^4.17.1", + "graphql": "^15.3.0 || ^16.0.0" + } + }, + "node_modules/apollo-server-express/node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/apollo-server-express/node_modules/@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/apollo-server-express/node_modules/@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/apollo-server-plugin-base": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz", + "integrity": "sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw==", + "deprecated": "The `apollo-server-plugin-base` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", + "license": "MIT", + "dependencies": { + "apollo-server-types": "^3.8.0" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "graphql": "^15.3.0 || ^16.0.0" + } + }, + "node_modules/apollo-server-types": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.8.0.tgz", + "integrity": "sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A==", + "deprecated": "The `apollo-server-types` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", + "license": "MIT", + "dependencies": { + "@apollo/utils.keyvaluecache": "^1.0.1", + "@apollo/utils.logger": "^1.0.0", + "apollo-reporting-protobuf": "^3.4.0", + "apollo-server-env": "^4.2.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "graphql": "^15.3.0 || ^16.0.0" + } + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, + "node_modules/argon2": { + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.28.7.tgz", + "integrity": "sha512-pvsScM3Fq7b+jolXkZHh8nRQx0uD/WeelnwYPMRpn4pAydoa1gqeL/KRdWAag4Hnu1TJNBTAfqyTjV+ZHwNnYA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.9", + "@phc/format": "^1.0.0", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0", + "optional": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1691.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1691.0.tgz", + "integrity": "sha512-/F2YC+DlsY3UBM2Bdnh5RLHOPNibS/+IcjUuhP8XuctyrN+MlL+fWDAiela32LTDk7hMy4rx8MTgvbJ+0blO5g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT", + "optional": true + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "dependencies": { + "dicer": "0.3.0" + }, + "engines": { + "node": ">=4.5.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "license": "MIT", + "optional": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "optional": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/connect-redis": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz", + "integrity": "sha512-aaNluLlAn/3JPxRwdzw7lhvEoU6Enb+d83xnokUNhC9dktqBoawKWL+WuxinxvBLTz6q9vReTnUDnUslaz74aw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==", + "license": "MIT" + }, + "node_modules/dataloader": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", + "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT", + "optional": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "dependencies": { + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=4.5.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-safe": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv-safe/-/dotenv-safe-8.2.0.tgz", + "integrity": "sha512-uWwWWdUQkSs5a3mySDB22UtNwyEYi0JtEQu+vDzIqr9OjbDdC2Ip13PnSpi/fctqlYmzkxCeabiyCAOROuAIaA==", + "license": "MIT", + "dependencies": { + "dotenv": "^8.2.0" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/ent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", + "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "license": "MIT", + "dependencies": { + "cookie": "0.6.0", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT", + "optional": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT", + "optional": true + }, + "node_modules/fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/firebase-admin": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.11.1.tgz", + "integrity": "sha512-UyEbq+3u6jWzCYbUntv/HuJiTixwh36G1R9j0v71mSvGAx/YZEWEW7uSGLYxBYE6ckVRQoKMr40PYUEzrm/4dg==", + "license": "Apache-2.0", + "dependencies": { + "@fastify/busboy": "^1.2.1", + "@firebase/database-compat": "^0.3.4", + "@firebase/database-types": "^0.10.4", + "@types/node": ">=12.12.47", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.0.1", + "node-forge": "^1.3.1", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^6.8.0", + "@google-cloud/storage": "^6.9.5" + } + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/fireorm": { + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/fireorm/-/fireorm-0.23.3.tgz", + "integrity": "sha512-HQB/WyHZp0Khoxp7AZFpwwbGznVMesuVuRl4QR74UEIN6UZkKUASOFdT+d0Pkif5W2oKQmVdd0mFsJiLxEuCJg==", + "license": "ISC", + "dependencies": { + "class-transformer": "0.4.0", + "pluralize": "^8.0.0", + "ts-object-path": "^0.1.2" + }, + "peerDependencies": { + "reflect-metadata": "^0.1.13" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-capacitor": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-8.0.0.tgz", + "integrity": "sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA==", + "license": "MIT", + "engines": { + "node": "^14.17.0 || >=16.0.0" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT", + "optional": true + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gen-env-types": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/gen-env-types/-/gen-env-types-1.3.4.tgz", + "integrity": "sha512-pG3SEiF2WwK9INywDHLouAVOZPTc+XeDEhr/FW+y2P2i5zEbs4uTU+xhfekCayuaSMfV/hUFXczFsTSC7lVOEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "bin": { + "gen-env-types": "gen-env-types.js" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-gax": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.6.1.tgz", + "integrity": "sha512-g/lcUjGcB6DSw2HxgEmCDOrI/CByOwqRvsuUvNalHUK2iPPPlmAIpbMbl62u0YufGMr8zgE3JL7th6dCb1Ry+w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "~1.8.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "@types/rimraf": "^3.0.2", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^8.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^1.0.0", + "protobufjs": "7.2.4", + "protobufjs-cli": "1.1.1", + "retry-request": "^5.0.0" + }, + "bin": { + "compileProtos": "build/tools/compileProtos.js", + "minifyProtoJson": "build/tools/minify.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/google-gax/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/google-gax/node_modules/protobufjs": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", + "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "deprecated": "Package is no longer maintained", + "license": "MIT", + "optional": true, + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, + "node_modules/graphql": { + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.9.0.tgz", + "integrity": "sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==", + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/graphql-query-complexity": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/graphql-query-complexity/-/graphql-query-complexity-0.7.2.tgz", + "integrity": "sha512-+VgmrfxGEjHI3zuojWOR8bsz7Ycz/BZjNjxnlUieTz5DsB92WoIrYCSZdWG7UWZ3rfcA1Gb2Nf+wB80GsaZWuQ==", + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2" + }, + "peerDependencies": { + "graphql": "^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/graphql-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz", + "integrity": "sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g==", + "license": "MIT", + "dependencies": { + "iterall": "^1.3.0" + }, + "peerDependencies": { + "graphql": "^0.10.5 || ^0.11.3 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/graphql-upload": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-13.0.0.tgz", + "integrity": "sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==", + "license": "MIT", + "dependencies": { + "busboy": "^0.3.1", + "fs-capacitor": "^6.2.0", + "http-errors": "^1.8.1", + "object-path": "^0.11.8" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >= 16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/jaydenseric" + }, + "peerDependencies": { + "graphql": "0.13.1 - 16" + } + }, + "node_modules/graphql-upload/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/graphql-upload/node_modules/fs-capacitor": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-6.2.0.tgz", + "integrity": "sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/graphql-upload/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/graphql-upload/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "license": "BSD-3-Clause" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ioredis": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ioredis/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ioredis/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", + "license": "MIT", + "optional": true + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", + "license": "MIT" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.3.tgz", + "integrity": "sha512-Nu7Sf35kXJ1MWDZIMAuATRQTg1iIPdzh7tqJ6jjvaU/GfDf+qi5UV8zJR3Mo+/pYFvm8mzay4+6O5EWigaQBQw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "optional": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "optional": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "license": "MIT", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/legacy-swc-helpers": { + "name": "@swc/helpers", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", + "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "license": "MIT", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.11.tgz", + "integrity": "sha512-mF3KaORjJQR6JBNcOkluDcJKhtoQT4VTLRMrX1v/wlBayL4M8ybwEDeryyPcrSEJmD0rVwHUbBarpZwN5NfPFQ==", + "license": "MIT", + "peer": true + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT", + "optional": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "license": "MIT" + }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "license": "ISC" + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "optional": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "license": "Unlicense", + "optional": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "license": "MIT", + "optional": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT", + "optional": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/nodemailer": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", + "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-path": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", + "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", + "license": "MIT", + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "license": "MIT", + "optional": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz", + "integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "optional": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proto3-json-serializer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz", + "integrity": "sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "protobufjs": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs-cli": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz", + "integrity": "sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^4.0.0", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "protobufjs": "^7.0.0" + } + }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT", + "optional": true + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redis": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", + "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.0", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-session": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/redis-session/-/redis-session-0.1.0.tgz", + "integrity": "sha512-quhgvNQzi1+4FITWJAsQcMhHl1NBgTHADTdsTDfszwtW5Fo3AgE+Pw7TVCNRxsei+gqAIhJ5R45Qaz4qc1nDuA==", + "dependencies": { + "redis": ">= 0.0.1" + }, + "engines": { + "node": ">= 0.1.98" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0" + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "license": "MIT", + "optional": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", + "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.1.1", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/retry-request/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/retry-request/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", + "optional": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT", + "optional": true + }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT", + "optional": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/teeny-request": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz", + "integrity": "sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/text-decoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", + "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==", + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-object-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ts-object-path/-/ts-object-path-0.1.2.tgz", + "integrity": "sha512-YLX9omkbe2spFv7DDo1fC5BiRfMtu/LgYuGSXnuIT5EkhedzCEShT7YrsQV3pjjT7doY2RHfoxJrra16KJlf7w==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-graphql": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/type-graphql/-/type-graphql-1.1.1.tgz", + "integrity": "sha512-iOOWVn0ehCYMukmnXStbkRwFE9dcjt7/oDcBS1JyQZo9CbhlIll4lHHps54HMEk4A4c8bUPd+DjK8w1/ZrxB4A==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.3", + "@types/node": "*", + "@types/semver": "^7.3.3", + "glob": "^7.1.6", + "graphql-query-complexity": "^0.7.0", + "graphql-subscriptions": "^1.1.0", + "semver": "^7.3.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 10.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typegraphql" + }, + "peerDependencies": { + "class-validator": ">=0.12.0", + "graphql": "^15.3.0" + } + }, + "node_modules/type-graphql/node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/type-graphql/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typeorm": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", + "license": "MIT", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.2.1", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/typeorm/node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT", + "optional": true + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "license": "MIT", + "optional": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/value-or-promise": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz", + "integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", + "license": "MIT", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ffabccd --- /dev/null +++ b/package.json @@ -0,0 +1,62 @@ +{ + "name": "comroots-backend", + "version": "1.0.0", + "description": "Backend for Comroots. Uses PostgreSQL, Redis, and ExpressJS. Hosted as a Docker container using Dokku.", + "main": "index.js", + "type": "commonjs", + "scripts": { + "dev": "nodemon --watch . src/index.ts", + "start": "ts-node src/index.ts", + "env:generate": "gen-env-types .env -o env.d.ts -e .", + "typeorm": "ts-node ./node_modules/typeorm/cli.js", + "migration:initial": "run typeorm migration:generate -d src/type-orm.source.ts src/migrations/Initial", + "migration:entities-update": "run typeorm migration:generate -d src/type-orm.source.ts src/migrations/EntitiesUpdate", + "migration:new": "run typeorm migration:create" + }, + "dependencies": { + "@faker-js/faker": "^7.4.0", + "@types/graphql-upload": "^8.0.11", + "apollo-server-core": "^3.8.2", + "apollo-server-errors": "^3.3.1", + "apollo-server-express": "^3.8.2", + "argon2": "^0.28.5", + "aws-sdk": "^2.1172.0", + "connect-redis": "^6.1.3", + "dataloader": "^2.1.0", + "dotenv-safe": "^8.2.0", + "express": "^4.18.1", + "express-session": "^1.17.3", + "firebase-admin": "^11.0.0", + "fireorm": "^0.23.3", + "graphql": "^15.3.0", + "graphql-upload": "13.0.0", + "ioredis": "^5.1.0", + "lodash": "^4.17.21", + "nodemailer": "^6.7.5", + "pg": "^8.7.3", + "redis-session": "^0.1.0", + "reflect-metadata": "^0.1.13", + "type-graphql": "^1.1.1", + "typeorm": "^0.3.7", + "uuid": "^8.3.2", + "@swc/core": "^1.2.213", + "@swc/helpers": "^0.4.3", + "ts-node": "^10.8.2", + "tsconfig-paths": "^4.0.0", + "regenerator-runtime": "^0.13.9" + }, + "devDependencies": { + "@types/connect-redis": "^0.0.18", + "@types/dotenv-safe": "^8.1.2", + "@types/express": "^4.17.13", + "@types/express-session": "^1.17.4", + "@types/ioredis": "^4.28.10", + "@types/lodash": "^4.14.182", + "@types/node": "^17.0.41", + "@types/nodemailer": "^6.4.4", + "@types/uuid": "^8.3.4", + "gen-env-types": "^1.3.4", + "nodemon": "^2.0.16", + "typescript": "^4.7.3" + } +} diff --git a/src/collections/Conversation.ts b/src/collections/Conversation.ts new file mode 100644 index 0000000..d29db08 --- /dev/null +++ b/src/collections/Conversation.ts @@ -0,0 +1,35 @@ +import { Collection, getRepository, ISubCollection, SubCollection } from 'fireorm' +import { Field, Int, ObjectType } from 'type-graphql' + +@ObjectType() +export class Message { + @Field() + id: string + + @Field(() => String) + createdAt: Date + + @Field(() => String) + updatedAt: Date + + @Field(() => String) + type: 'text' | 'image' | 'video' | 'audio' | 'file' + + @Field(() => String) + content: string + + @Field(() => Int) + senderId: number +} + +@ObjectType() +@Collection('conversations') +export class ConversationInFirestore { + @Field() + id: string + + @SubCollection(Message, 'messages') + messages: ISubCollection +} + +export const ConversationCol = getRepository(ConversationInFirestore) \ No newline at end of file diff --git a/src/collections/Inbox.ts b/src/collections/Inbox.ts new file mode 100644 index 0000000..705257f --- /dev/null +++ b/src/collections/Inbox.ts @@ -0,0 +1,17 @@ +import { Collection, getRepository } from 'fireorm' +import { ObjectType, Field, Int } from 'type-graphql' + +@ObjectType() +@Collection('inboxes') +export class Inbox { + @Field() + id: string + + @Field(() => Int) + userId: number + + @Field(() => [String]) + conversationIds: string[] +} + +export const InboxCol = getRepository(Inbox) \ No newline at end of file diff --git a/src/collections/index.ts b/src/collections/index.ts new file mode 100644 index 0000000..088703a --- /dev/null +++ b/src/collections/index.ts @@ -0,0 +1,2 @@ +export * from './Conversation' +export * from './Inbox' \ No newline at end of file diff --git a/src/configs/aws.config.ts b/src/configs/aws.config.ts new file mode 100644 index 0000000..b289676 --- /dev/null +++ b/src/configs/aws.config.ts @@ -0,0 +1,12 @@ +import AWS from 'aws-sdk' + +AWS.config.update({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY + } +}) + +export const s3 = new AWS.S3({ apiVersion: '2006-03-01' }) +export const quicksight = new AWS.QuickSight() \ No newline at end of file diff --git a/src/configs/firebase.config.ts b/src/configs/firebase.config.ts new file mode 100644 index 0000000..e9fb2d7 --- /dev/null +++ b/src/configs/firebase.config.ts @@ -0,0 +1,14 @@ +import admin from 'firebase-admin' +import { getFirestore } from 'firebase-admin/firestore' +import * as fireorm from 'fireorm' + +const app = admin.initializeApp({ + credential: admin.credential.cert({ + clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL, + privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY, + projectId: process.env.FIREBASE_ADMIN_PROJECT_ID + }) +}) + +export const firestore = getFirestore(app) +fireorm.initialize(firestore) \ No newline at end of file diff --git a/src/configs/index.ts b/src/configs/index.ts new file mode 100644 index 0000000..bada395 --- /dev/null +++ b/src/configs/index.ts @@ -0,0 +1,3 @@ +export * from './aws.config' +export * from './type-orm.config' +export * from './firebase.config' \ No newline at end of file diff --git a/src/configs/type-orm.config.ts b/src/configs/type-orm.config.ts new file mode 100644 index 0000000..f734585 --- /dev/null +++ b/src/configs/type-orm.config.ts @@ -0,0 +1,39 @@ +import { Comment, CommentVote, Conversation, CV, EducationItem, Experience, Offer, OfferApplication, Page, PageFollow, Post, Qualification, Space, SpaceSubscription, Tag, Vote, User, UserFollow } from '@entities' +import path from 'path' +import { DataSource } from 'typeorm' + +export const typeORMConfig = { + type: 'postgres', + username: process.env.DB_USERNAME, + password: process.env.DB_PASSWORD, + host: process.env.DB_HOST, + port: parseInt(process.env.DB_PORT), + database: process.env.DB_DATABASE, + logging: true, + synchronize: false, + entities: [ + User, + Post, + Vote, + Space, + SpaceSubscription, + Comment, + CommentVote, + Offer, + OfferApplication, + Tag, + Conversation, + Page, + PageFollow, + UserFollow, + EducationItem, + Experience, + Qualification, + CV + ], + migrationsTableName: 'type_orm_migrations', + migrations: [path.join(__dirname, '../migrations/*')], + cli: { + migrationsDir: 'migrations' + } +} as ConstructorParameters[0] \ No newline at end of file diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..36b3619 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,4 @@ +// export const __prod__ = process.env.NODE_ENV === 'production' +export const __prod__ = false +export const COOKIE_NAME = 'qid' +export const FORGET_PASSWORD_PREFIX = 'forget-password:' \ No newline at end of file diff --git a/src/entities/CV.ts b/src/entities/CV.ts new file mode 100644 index 0000000..059f86e --- /dev/null +++ b/src/entities/CV.ts @@ -0,0 +1,30 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm' +import { User } from './User' + +@ObjectType() +@Entity() +export class CV extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field() + @Column() + filename?: string + + @Field() + @Column() + key?: string + + @Field({ nullable: true }) + url?: string + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + userId?: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.cvs, { onDelete: 'CASCADE' }) + user?: User +} \ No newline at end of file diff --git a/src/entities/Comment.ts b/src/entities/Comment.ts new file mode 100644 index 0000000..c9800d0 --- /dev/null +++ b/src/entities/Comment.ts @@ -0,0 +1,60 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' +import { CommentVote, Post, User, Page } from '@entities' + +@ObjectType() +@Entity() +export class Comment extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => String, { nullable: true }) + @CreateDateColumn() + createdAt?: Date + + @Field(() => String, { nullable: true }) + @UpdateDateColumn() + updatedAt?: Date + + @Field() + @Column() + text!: string + + @Field(() => String) + @Column({ default: 'user' }) + creatorType?: 'page' | 'user' + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + creatorId?: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.comments, { onDelete: 'CASCADE' }) + creator?: User + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + pageCreatorId?: number + + @Field(() => Page, { nullable: true }) + @ManyToOne(() => Page, page => page.posts, { onDelete: 'CASCADE' }) + pageCreator?: Page + + @Field(() => Int, { nullable: true }) + @Column({ nullable: false }) + postId?: number + + @ManyToOne(() => Post, post => post.comments, { onDelete: 'CASCADE' }) + post?: Post + + @OneToMany(() => CommentVote, commentVote => commentVote.comment) + votes?: CommentVote[] + + @Field(() => Int) + @Column({ type: 'int', default: 0 }) + points!: number + + @Field(() => Int, { nullable: true }) + voteStatus?: number +} \ No newline at end of file diff --git a/src/entities/CommentVote.ts b/src/entities/CommentVote.ts new file mode 100644 index 0000000..5f3bbf8 --- /dev/null +++ b/src/entities/CommentVote.ts @@ -0,0 +1,27 @@ +import { Comment, User } from '@entities' +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class CommentVote extends BaseEntity { + @Field(() => Int) + @Column({ type: 'int' }) + value!: number + + @Field(() => Int) + @PrimaryColumn() + userId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.commentVotes, { onDelete: 'CASCADE' }) + user?: User + + @Field(() => Int) + @PrimaryColumn() + commentId!: number + + @Field(() => Comment) + @ManyToOne(() => Comment, comment => comment.votes, { onDelete: 'CASCADE' }) + comment?: Comment +} \ No newline at end of file diff --git a/src/entities/Conversation.ts b/src/entities/Conversation.ts new file mode 100644 index 0000000..4d19fa4 --- /dev/null +++ b/src/entities/Conversation.ts @@ -0,0 +1,18 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class Conversation extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id: number + + @Field(() => [Int]) + @Column('int', { array: true }) + participants: number[] + + @Field() + @Column() + firestoreCollectionId: string +} \ No newline at end of file diff --git a/src/entities/EducationItem.ts b/src/entities/EducationItem.ts new file mode 100644 index 0000000..d91b231 --- /dev/null +++ b/src/entities/EducationItem.ts @@ -0,0 +1,42 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm' +import { User } from './User' + +@ObjectType() +@Entity() +export class EducationItem extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field() + @Column() + school?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + status?: string + + @Field(() => Date, { nullable: true }) + @Column({ nullable: true, type: 'timestamptz' }) + startDate?: Date + + @Field(() => Date, { nullable: true }) + @Column({ nullable: true, type: 'timestamptz' }) + endDate?: Date + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + userId?: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.educationItems, { onDelete: 'CASCADE' }) + user?: User + + @Field({ nullable: true }) + @Column({ nullable: true }) + photo?: string + + @Field({ nullable: true }) + photoUrl?: string +} \ No newline at end of file diff --git a/src/entities/Experience.ts b/src/entities/Experience.ts new file mode 100644 index 0000000..2552b14 --- /dev/null +++ b/src/entities/Experience.ts @@ -0,0 +1,42 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm' +import { User } from './User' + +@ObjectType() +@Entity() +export class Experience extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field() + @Column() + title?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + workplace?: string + + @Field(() => Date, { nullable: true }) + @Column({ nullable: true, type: 'timestamptz' }) + startDate?: Date + + @Field(() => Date, { nullable: true }) + @Column({ nullable: true, type: 'timestamptz' }) + endDate?: Date + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + userId?: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.experiences, { onDelete: 'CASCADE' }) + user?: User + + @Field({ nullable: true }) + @Column({ nullable: true }) + photo?: string + + @Field({ nullable: true }) + photoUrl?: string +} \ No newline at end of file diff --git a/src/entities/Offer.ts b/src/entities/Offer.ts new file mode 100644 index 0000000..39154fe --- /dev/null +++ b/src/entities/Offer.ts @@ -0,0 +1,104 @@ +import { OfferApplication, Page, Space, User } from '@entities' +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class Offer extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => String) + @CreateDateColumn() + createdAt?: Date + + @Field(() => String) + @UpdateDateColumn() + updatedAt?: Date + + @Field(() => String, { nullable: true }) + @Column({ default: 'user' }) + creatorType?: 'page' | 'user' + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + creatorId!: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.offers, { onDelete: 'CASCADE' }) + creator?: User + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + pageCreatorId?: number + + @Field(() => Page, { nullable: true }) + @ManyToOne(() => Page, page => page.offers, { onDelete: 'CASCADE' }) + pageCreator?: Page + + @Field(() => Int) + @Column() + spaceId!: number + + @Field(() => Space, { nullable: true }) + @ManyToOne(() => Space, space => space.posts, { onDelete: 'CASCADE' }) + space?: Space + + @Field() + @Column() + title: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + workplace?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + address?: string + + @Field({ nullable: true }) + @Column({ nullable: false, default: false }) + recruiting?: boolean + + @Field(() => [OfferApplication], { nullable: true }) + @OneToMany(() => OfferApplication, offerApplication => offerApplication.offer) + applications: OfferApplication[] + + @Field({ nullable: true }) + @Column({ nullable: true }) + employmentType?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + salaryRange?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + department?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + requirements?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + benefits?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + description?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + photo?: string + + @Field({ nullable: true }) + photoUrl?: string + + @Field(() => Int, { nullable: true }) + applicationsNo?: number + + @Field(() => String, { nullable: true }) + applicationStatus?: 'applied' | 'accepted' | 'rejected' +} \ No newline at end of file diff --git a/src/entities/OfferApplication.ts b/src/entities/OfferApplication.ts new file mode 100644 index 0000000..279e078 --- /dev/null +++ b/src/entities/OfferApplication.ts @@ -0,0 +1,31 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm' +import { User, Offer } from '@entities' + +@ObjectType() +@Entity() +export class OfferApplication extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => Int) + @Column() + userId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.applications, { onDelete: 'CASCADE', cascade: true }) + user?: User + + @Field(() => Int) + @Column() + offerId!: number + + @Field(() => Offer) + @ManyToOne(() => Offer, offer => offer.applications, { onDelete: 'CASCADE', cascade: true }) + offer?: Offer + + @Field() + @Column({ default: 'applied' }) + status!: 'applied' | 'accepted' | 'rejected' +} \ No newline at end of file diff --git a/src/entities/Page.ts b/src/entities/Page.ts new file mode 100644 index 0000000..92a819d --- /dev/null +++ b/src/entities/Page.ts @@ -0,0 +1,84 @@ +import { Comment, Offer, Post, User, PageFollow } from '@entities' +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, CreateDateColumn, Entity, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class Page extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => String, { nullable: true }) + @CreateDateColumn() + createdAt?: Date + + @Field(() => String, { nullable: true }) + @UpdateDateColumn() + updatedAt?: Date + + @Field(() => [User], { nullable: true }) + @ManyToMany(() => User, { cascade: true }) + @JoinTable() + owners?: User[] + + @Field(() => Boolean, { nullable: true }) + ownerStatus?: boolean + + @OneToMany(() => PageFollow, pageFollow => pageFollow.page) + pageFollows?: PageFollow[] + + @Field(() => Boolean, { nullable: true }) + followStatus?: boolean + + @Field(() => Int) + followerNumber?: number + + @Field() + @Column({ unique: true, nullable: false }) + pageName?: string + + @Field({ nullable: true }) + @Column({ unique: true, nullable: true }) + email?: string + + @OneToMany(() => Post, post => post.pageCreator) + posts?: Post[] + + @OneToMany(() => Offer, offer => offer.pageCreator) + offers?: Offer[] + + @Field(() => [Comment], { nullable: true }) + @OneToMany(() => Comment, comment => comment.creator) + comments?: Comment[] + + @Field({ nullable: true }) + @Column({ nullable: true }) + avatar?: string + + @Field({ nullable: true }) + avatarUrl?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + coverPhoto?: string + + @Field({ nullable: true }) + coverPhotoUrl?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + fullPageName?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + headline?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + address?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + about?: string +} \ No newline at end of file diff --git a/src/entities/PageFollow.ts b/src/entities/PageFollow.ts new file mode 100644 index 0000000..5050a58 --- /dev/null +++ b/src/entities/PageFollow.ts @@ -0,0 +1,24 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Entity, ManyToOne, PrimaryColumn } from 'typeorm' +import { Page } from './Page' +import { User } from './User' + +@ObjectType() +@Entity() +export class PageFollow extends BaseEntity { + @Field(() => Int) + @PrimaryColumn() + userId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.pageFollows, { onDelete: 'CASCADE' }) + user?: User + + @Field(() => Int) + @PrimaryColumn() + pageId!: number + + @Field(() => Page) + @ManyToOne(() => Page, page => page.pageFollows, { onDelete: 'CASCADE' }) + page?: Page +} \ No newline at end of file diff --git a/src/entities/Post.ts b/src/entities/Post.ts new file mode 100644 index 0000000..be4e0a3 --- /dev/null +++ b/src/entities/Post.ts @@ -0,0 +1,73 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, CreateDateColumn, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' +import { Vote, User, Space, Comment, Tag, Page } from '@entities' + +@ObjectType() +@Entity() +export class Post extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => String) + @CreateDateColumn() + createdAt?: Date + + @Field(() => String) + @UpdateDateColumn() + updatedAt?: Date + + @Field() + @Column() + title!: string + + @Field() + @Column() + text!: string + + @Field(() => String) + @Column({ default: 'user' }) + creatorType?: 'page' | 'user' + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + creatorId?: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' }) + creator?: User + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + pageCreatorId?: number + + @Field(() => Page, { nullable: true }) + @ManyToOne(() => Page, page => page.posts, { onDelete: 'CASCADE' }) + pageCreator?: Page + + @OneToMany(() => Vote, vote => vote.post) + votes?: Vote[] + + @Field(() => Int) + @Column({ type: 'int', default: 0 }) + points!: number + + @Field(() => Int, { nullable: true }) + voteStatus?: number + + @Field(() => Int) + @Column() + spaceId!: number + + @Field(() => Space) + @ManyToOne(() => Space, space => space.posts, { onDelete: 'CASCADE' }) + space!: Space + + @OneToMany(() => Comment, comment => comment.post) + comments?: Comment[] + + @Field(() => [Tag], { nullable: true }) + @ManyToMany(() => Tag, { cascade: true }) + @JoinTable() // Put @JoinTable() on owner side of a MTM relationship + tags: Tag[] +} \ No newline at end of file diff --git a/src/entities/Qualification.ts b/src/entities/Qualification.ts new file mode 100644 index 0000000..70b2469 --- /dev/null +++ b/src/entities/Qualification.ts @@ -0,0 +1,54 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm' +import { User } from './User' + +@ObjectType() +@Entity() +export class Qualification extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field() + @Column() + name!: string + + @Field() + @Column() + issuingOrganisation!: string + + @Field(() => Date, { nullable: true }) + @Column({ nullable: true, type: 'timestamptz' }) + issuanceDate?: Date + + @Field() + @Column() + expire!: boolean + + @Field(() => Date, { nullable: true }) + @Column({ nullable: true, type: 'timestamptz' }) + expirationDate?: Date + + @Field({ nullable: true }) + @Column({ nullable: true }) + credentialID?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + credentialURL?: string + + @Field(() => Int, { nullable: true }) + @Column({ nullable: true }) + userId?: number + + @Field(() => User, { nullable: true }) + @ManyToOne(() => User, user => user.qualifications, { onDelete: 'CASCADE' }) + user?: User + + @Field({ nullable: true }) + @Column({ nullable: true }) + photo?: string + + @Field({ nullable: true }) + photoUrl?: string +} \ No newline at end of file diff --git a/src/entities/Space.ts b/src/entities/Space.ts new file mode 100644 index 0000000..a812eab --- /dev/null +++ b/src/entities/Space.ts @@ -0,0 +1,73 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, CreateDateColumn, Entity, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' +import { Post, SpaceSubscription, User } from '@entities' + +@ObjectType() +@Entity() +export class Space extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => String) + @CreateDateColumn() + createdAt?: Date + + @Field(() => String) + @UpdateDateColumn() + updatedAt?: Date + + @Field() + @Column({ unique: true }) + spaceName!: string + + @OneToMany(() => Post, post => post.space) + posts?: Post[] + + @OneToMany(() => SpaceSubscription, spaceSubscription => spaceSubscription.space) + spaceSubscriptions?: SpaceSubscription[] + + @Field(() => Boolean, { nullable: true }) + subscriptionStatus?: boolean + + @Field(() => Int) + subscriberNumber?: number + + @Field(() => Boolean) + modStatus?: boolean + + @Field(() => [User], { nullable: true }) + @ManyToMany(() => User, { cascade: true }) + @JoinTable() + mods?: User[] + + @Field({ nullable: true }) + @Column({ nullable: true }) + avatar?: string // S3 Key (Location) to image + + @Field({ nullable: true }) + avatarUrl?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + coverPhoto?: string + + @Field({ nullable: true }) + coverPhotoUrl?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + fullSpaceName?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + rules?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + headline?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + about?: string +} \ No newline at end of file diff --git a/src/entities/SpaceSubscription.ts b/src/entities/SpaceSubscription.ts new file mode 100644 index 0000000..f276df6 --- /dev/null +++ b/src/entities/SpaceSubscription.ts @@ -0,0 +1,23 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Entity, ManyToOne, PrimaryColumn } from 'typeorm' +import { Space, User } from '@entities' + +@ObjectType() +@Entity() +export class SpaceSubscription extends BaseEntity { + @Field(() => Int) + @PrimaryColumn() + userId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.spaceSubscriptions, { onDelete: 'CASCADE' }) + user?: User + + @Field(() => Int) + @PrimaryColumn() + spaceId!: number + + @Field(() => Space) + @ManyToOne(() => Space, space => space.spaceSubscriptions, { onDelete: 'CASCADE' }) + space?: Space +} \ No newline at end of file diff --git a/src/entities/Tag.ts b/src/entities/Tag.ts new file mode 100644 index 0000000..4a9bbc4 --- /dev/null +++ b/src/entities/Tag.ts @@ -0,0 +1,14 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class Tag extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id: number + + @Field() + @Column() + name: string +} \ No newline at end of file diff --git a/src/entities/User.ts b/src/entities/User.ts new file mode 100644 index 0000000..7ebbdb2 --- /dev/null +++ b/src/entities/User.ts @@ -0,0 +1,124 @@ +import { Comment, CommentVote, CV, EducationItem, Experience, Offer, OfferApplication, PageFollow, Post, Qualification, SpaceSubscription, UserFollow, Vote } from '@entities' +import { Field, Float, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class User extends BaseEntity { + @Field(() => Int) + @PrimaryGeneratedColumn() + id!: number + + @Field(() => String, { nullable: true }) + @CreateDateColumn() + createdAt?: Date + + @Field(() => String, { nullable: true }) + @UpdateDateColumn() + updatedAt?: Date + + @Field() + @Column({ unique: true, nullable: false }) + username?: string + + @Field({ nullable: true }) + @Column({ unique: true, nullable: false }) + email?: string + + @Column({ nullable: false }) + password?: string + + @OneToMany(() => Post, post => post.creator) + posts?: Post[] + + @OneToMany(() => Offer, offer => offer.creator) + offers?: Offer[] + + @OneToMany(() => EducationItem, educationItem => educationItem.user, { cascade: true }) + educationItems?: EducationItem[] + + @OneToMany(() => Experience, experience => experience.user, { cascade: true }) + experiences?: Experience[] + + @OneToMany(() => Qualification, qualification => qualification.user, { cascade: true }) + qualifications?: Qualification[] + + @Field(() => [String], { nullable: true }) + @Column(`simple-array`, { nullable: true }) + skills?: string[] + + @OneToMany(() => CV, cv => cv.user) + cvs?: CV[] + + @OneToMany(() => Vote, vote => vote.user) + votes?: Vote[] + + @OneToMany(() => SpaceSubscription, spaceSubscription => spaceSubscription.user) + spaceSubscriptions?: SpaceSubscription[] + + @OneToMany(() => PageFollow, pageFollow => pageFollow.user) + pageFollows?: PageFollow[] + + // One user can follow many + @OneToMany(() => UserFollow, userFollow => userFollow.followingUser) + userFollowings?: UserFollow[] + + // One user can be followed by many + @OneToMany(() => UserFollow, userFollow => userFollow.followedUser) + userFolloweds?: PageFollow[] + + @Field(() => [Comment], { nullable: true }) + @OneToMany(() => Comment, comment => comment.creator) + comments?: Comment[] + + @OneToMany(() => CommentVote, commentVote => commentVote.user) + commentVotes?: CommentVote[] + + @Field(() => [OfferApplication], { nullable: true }) + @OneToMany(() => OfferApplication, offerApplication => offerApplication.user) + applications?: OfferApplication[] + + @Field({ nullable: true }) + @Column({ nullable: true }) + avatar?: string // S3 Key (Location) to image + + @Field({ nullable: true }) + avatarUrl?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + coverPhoto?: string + + @Field({ nullable: true }) + coverPhotoUrl?: string + + @Field(() => Int) + followerNumber?: number + + @Field(() => Int) + followingNumber?: number + + @Field({ nullable: true }) + @Column({ nullable: true }) + fullName?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + headline?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + address?: string + + @Field({ nullable: true }) + @Column({ nullable: true }) + about?: string + + @Field(() => Float, { nullable: true }) + @Column({ type: 'float', nullable: true }) + mostRecentLatitude?: number + + @Field(() => Float, { nullable: true }) + @Column({ type:'float', nullable: true }) + mostRecentLongitude?: number +} \ No newline at end of file diff --git a/src/entities/UserFollow.ts b/src/entities/UserFollow.ts new file mode 100644 index 0000000..417bc4a --- /dev/null +++ b/src/entities/UserFollow.ts @@ -0,0 +1,23 @@ +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Entity, ManyToOne, PrimaryColumn } from 'typeorm' +import { User } from './User' + +@ObjectType() +@Entity() +export class UserFollow extends BaseEntity { + @Field(() => Int) + @PrimaryColumn() + followingUserId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.userFollowings, { onDelete: 'CASCADE' }) + followingUser?: User + + @Field(() => Int) + @PrimaryColumn() + followedUserId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.userFolloweds, { onDelete: 'CASCADE' }) + followedUser?: User +} \ No newline at end of file diff --git a/src/entities/Vote.ts b/src/entities/Vote.ts new file mode 100644 index 0000000..ada1e41 --- /dev/null +++ b/src/entities/Vote.ts @@ -0,0 +1,27 @@ +import { Post, User } from '@entities' +import { Field, Int, ObjectType } from 'type-graphql' +import { BaseEntity, Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm' + +@ObjectType() +@Entity() +export class Vote extends BaseEntity { + @Field(() => Int) + @Column({ type: 'int' }) + value!: number + + @Field(() => Int) + @PrimaryColumn() + userId!: number + + @Field(() => User) + @ManyToOne(() => User, user => user.votes, { onDelete: 'CASCADE' }) + user?: User + + @Field(() => Int) + @PrimaryColumn() + postId!: number + + @Field(() => Post) + @ManyToOne(() => Post, post => post.votes, { onDelete: 'CASCADE' }) + post?: Post +} \ No newline at end of file diff --git a/src/entities/index.ts b/src/entities/index.ts new file mode 100644 index 0000000..3a8e072 --- /dev/null +++ b/src/entities/index.ts @@ -0,0 +1,18 @@ +export * from './User' +export * from './Post' +export * from './Vote' +export * from './Space' +export * from './SpaceSubscription' +export * from './Comment' +export * from './CommentVote' +export * from './OfferApplication' +export * from './Offer' +export * from './Tag' +export * from './Conversation' +export * from './Page' +export * from './PageFollow' +export * from './UserFollow' +export * from './EducationItem' +export * from './Experience' +export * from './Qualification' +export * from './CV' \ No newline at end of file diff --git a/src/graphql-types/CreateConversationReponse.ts b/src/graphql-types/CreateConversationReponse.ts new file mode 100644 index 0000000..861b93a --- /dev/null +++ b/src/graphql-types/CreateConversationReponse.ts @@ -0,0 +1,12 @@ +import { ConversationInFirestore } from '@collections' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class CreateConversationResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => ConversationInFirestore, { nullable: true }) + conversation?: ConversationInFirestore +} \ No newline at end of file diff --git a/src/graphql-types/EducationItemInput.ts b/src/graphql-types/EducationItemInput.ts new file mode 100644 index 0000000..e466d19 --- /dev/null +++ b/src/graphql-types/EducationItemInput.ts @@ -0,0 +1,20 @@ +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Field, InputType } from 'type-graphql' + +@InputType() +export class EducationItemInput { + @Field({ nullable: true }) + school?: string + + @Field({ nullable: true }) + status?: string + + @Field(() => Date, { nullable: true }) + startDate?: Date + + @Field(() => Date, { nullable: true }) + endDate?: Date + + @Field(() => GraphQLUpload, { nullable: true }) + photo?: Promise +} \ No newline at end of file diff --git a/src/graphql-types/EducationItemResponse.ts b/src/graphql-types/EducationItemResponse.ts new file mode 100644 index 0000000..8583301 --- /dev/null +++ b/src/graphql-types/EducationItemResponse.ts @@ -0,0 +1,12 @@ +import { EducationItem } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class EducationItemResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => EducationItem, { nullable: true }) + educationItem?: EducationItem +} \ No newline at end of file diff --git a/src/graphql-types/ExperienceInput.ts b/src/graphql-types/ExperienceInput.ts new file mode 100644 index 0000000..0b57c2a --- /dev/null +++ b/src/graphql-types/ExperienceInput.ts @@ -0,0 +1,20 @@ +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Field, InputType } from 'type-graphql' + +@InputType() +export class ExperienceInput { + @Field({ nullable: true }) + title?: string + + @Field({ nullable: true }) + workplace?: string + + @Field(() => Date, { nullable: true }) + startDate?: Date + + @Field(() => Date, { nullable: true }) + endDate?: Date + + @Field(() => GraphQLUpload, { nullable: true }) + photo?: Promise +} \ No newline at end of file diff --git a/src/graphql-types/ExperienceResponse.ts b/src/graphql-types/ExperienceResponse.ts new file mode 100644 index 0000000..9b149f0 --- /dev/null +++ b/src/graphql-types/ExperienceResponse.ts @@ -0,0 +1,12 @@ +import { Experience } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class ExperienceResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Experience, { nullable: true }) + experience?: Experience +} \ No newline at end of file diff --git a/src/graphql-types/FieldError.ts b/src/graphql-types/FieldError.ts new file mode 100644 index 0000000..b761b13 --- /dev/null +++ b/src/graphql-types/FieldError.ts @@ -0,0 +1,10 @@ +import { ObjectType, Field } from 'type-graphql' + +@ObjectType() +export class FieldError { + @Field() + field: string + + @Field() + message: string +} \ No newline at end of file diff --git a/src/graphql-types/ForgotPasswordResponse.ts b/src/graphql-types/ForgotPasswordResponse.ts new file mode 100644 index 0000000..311003f --- /dev/null +++ b/src/graphql-types/ForgotPasswordResponse.ts @@ -0,0 +1,11 @@ +import { ObjectType, Field } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class ForgotPasswordResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Boolean) + success: boolean +} \ No newline at end of file diff --git a/src/graphql-types/InboxResponse.ts b/src/graphql-types/InboxResponse.ts new file mode 100644 index 0000000..2548b87 --- /dev/null +++ b/src/graphql-types/InboxResponse.ts @@ -0,0 +1,11 @@ +import { User } from '@entities' +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +export class InboxResponse { + @Field(() => String) + firestoreCollectionId: string + + @Field(() => User) + partner: User +} \ No newline at end of file diff --git a/src/graphql-types/Location.ts b/src/graphql-types/Location.ts new file mode 100644 index 0000000..4f40e71 --- /dev/null +++ b/src/graphql-types/Location.ts @@ -0,0 +1,10 @@ +import { Field, Float, InputType } from 'type-graphql' + +@InputType() +export class Location { + @Field(() => Float, { nullable: true }) + latitude?: number + + @Field(() => Float, { nullable: true }) + longitude?: number +} \ No newline at end of file diff --git a/src/graphql-types/MessageInput.ts b/src/graphql-types/MessageInput.ts new file mode 100644 index 0000000..5ab22fc --- /dev/null +++ b/src/graphql-types/MessageInput.ts @@ -0,0 +1,14 @@ +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Field, InputType } from 'type-graphql' + +@InputType() +export class MessageInput { + @Field(() => String) + type!: 'text' | 'image' | 'video' | 'audio' | 'file' + + @Field(() => GraphQLUpload, { nullable: true }) + upload?: Promise + + @Field(() => String, { nullable: true }) + text?: string +} \ No newline at end of file diff --git a/src/graphql-types/OfferInput.ts b/src/graphql-types/OfferInput.ts new file mode 100644 index 0000000..ebd6e96 --- /dev/null +++ b/src/graphql-types/OfferInput.ts @@ -0,0 +1,80 @@ +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Field, InputType, Int } from 'type-graphql' + +@InputType() +export class OfferInputCreate { + @Field(() => Int, { nullable: true }) + pageId?: number + + @Field({ nullable: true }) + spaceName?: string + + @Field({ nullable: true }) + title?: string + + @Field({ nullable: true }) + workplace?: string + + @Field({ nullable: true }) + address?: string + + @Field({ nullable: true }) + recruiting?: boolean + + @Field({ nullable: true }) + employmentType?: string + + @Field({ nullable: true }) + salaryRange?: string + + @Field({ nullable: true }) + department?: string + + @Field({ nullable: true }) + requirements?: string + + @Field({ nullable: true }) + benefits?: string + + @Field({ nullable: true }) + description?: string + + @Field(() => GraphQLUpload, { nullable: true }) + photo?: Promise +} + +@InputType() +export class OfferInputUpdate { + @Field({ nullable: true }) + title?: string + + @Field({ nullable: true }) + workplace?: string + + @Field({ nullable: true }) + address?: string + + @Field({ nullable: true }) + recruiting?: boolean + + @Field({ nullable: true }) + employmentType?: string + + @Field({ nullable: true }) + salaryRange?: string + + @Field({ nullable: true }) + department?: string + + @Field({ nullable: true }) + requirements?: string + + @Field({ nullable: true }) + benefits?: string + + @Field({ nullable: true }) + description?: string + + @Field(() => GraphQLUpload, { nullable: true }) + photo?: Promise +} \ No newline at end of file diff --git a/src/graphql-types/OfferResponse.ts b/src/graphql-types/OfferResponse.ts new file mode 100644 index 0000000..a879a33 --- /dev/null +++ b/src/graphql-types/OfferResponse.ts @@ -0,0 +1,12 @@ +import { Offer } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class OfferResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Offer, { nullable: true }) + offer?: Offer +} \ No newline at end of file diff --git a/src/graphql-types/PageInfo.ts b/src/graphql-types/PageInfo.ts new file mode 100644 index 0000000..cb7c1d6 --- /dev/null +++ b/src/graphql-types/PageInfo.ts @@ -0,0 +1,16 @@ +import { Field, InputType } from 'type-graphql' + +@InputType() +export class PageInfo { + @Field({ nullable: true }) + fullPageName?: string + + @Field({ nullable: true }) + headline?: string + + @Field({ nullable: true }) + address?: string + + @Field({ nullable: true }) + about?: string +} \ No newline at end of file diff --git a/src/graphql-types/PageResponse.ts b/src/graphql-types/PageResponse.ts new file mode 100644 index 0000000..ff14430 --- /dev/null +++ b/src/graphql-types/PageResponse.ts @@ -0,0 +1,12 @@ +import { Page } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class PageResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Page, { nullable: true }) + page?: Page +} \ No newline at end of file diff --git a/src/graphql-types/PaginatedComments.ts b/src/graphql-types/PaginatedComments.ts new file mode 100644 index 0000000..7dfdefb --- /dev/null +++ b/src/graphql-types/PaginatedComments.ts @@ -0,0 +1,11 @@ +import { Comment } from '@entities' +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +export class PaginatedComments { + @Field(() => [Comment]) + comments: Comment[] + + @Field(() => Boolean) + hasMore: boolean +} \ No newline at end of file diff --git a/src/graphql-types/PaginatedOffers.ts b/src/graphql-types/PaginatedOffers.ts new file mode 100644 index 0000000..3bf10ab --- /dev/null +++ b/src/graphql-types/PaginatedOffers.ts @@ -0,0 +1,11 @@ +import { Offer } from '@entities' +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +export class PaginatedOffers { + @Field(() => [Offer]) + offers: Offer[] + + @Field(() => Boolean) + hasMore: boolean +} \ No newline at end of file diff --git a/src/graphql-types/PaginatedPosts.ts b/src/graphql-types/PaginatedPosts.ts new file mode 100644 index 0000000..a00c833 --- /dev/null +++ b/src/graphql-types/PaginatedPosts.ts @@ -0,0 +1,11 @@ +import { Post } from '@entities' +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +export class PaginatedPosts { + @Field(() => [Post]) + posts: Post[] + + @Field(() => Boolean) + hasMore: boolean +} \ No newline at end of file diff --git a/src/graphql-types/PhotoResponse.ts b/src/graphql-types/PhotoResponse.ts new file mode 100644 index 0000000..5ab9f2e --- /dev/null +++ b/src/graphql-types/PhotoResponse.ts @@ -0,0 +1,10 @@ +import { ObjectType, Field } from 'type-graphql' + +@ObjectType() +export class PhotoResponse { + @Field(() => String, { nullable: true }) + key?: string + + @Field(() => String, { nullable: true }) + url?: string +} \ No newline at end of file diff --git a/src/graphql-types/PostInput.ts b/src/graphql-types/PostInput.ts new file mode 100644 index 0000000..94944c1 --- /dev/null +++ b/src/graphql-types/PostInput.ts @@ -0,0 +1,31 @@ +import { Field, InputType, Int } from 'type-graphql' + +@InputType() +export class PostInputCreate { + @Field(() => Int, { nullable: true }) + pageId?: number + + @Field({ nullable: true }) + spaceName?: string + + @Field({ nullable: true }) + title?: string + + @Field({ nullable: true }) + text?: string + + @Field(() => [Int], { nullable: true }) + tags?: number[] +} + +@InputType() +export class PostInputUpdate { + @Field({ nullable: true }) + title?: string + + @Field({ nullable: true }) + text?: string + + @Field(() => [Int], { nullable: true }) + tags?: number[] +} \ No newline at end of file diff --git a/src/graphql-types/PostResponse.ts b/src/graphql-types/PostResponse.ts new file mode 100644 index 0000000..1dc7348 --- /dev/null +++ b/src/graphql-types/PostResponse.ts @@ -0,0 +1,12 @@ +import { Post } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class PostResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Post, { nullable: true }) + post?: Post +} \ No newline at end of file diff --git a/src/graphql-types/QualificationInput.ts b/src/graphql-types/QualificationInput.ts new file mode 100644 index 0000000..4150670 --- /dev/null +++ b/src/graphql-types/QualificationInput.ts @@ -0,0 +1,29 @@ +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Field, InputType } from 'type-graphql' + +@InputType() +export class QualificationInput { + @Field() + name!: string + + @Field() + issuingOrganisation!: string + + @Field(() => Date, { nullable: true }) + issuanceDate?: Date + + @Field() + expire: boolean + + @Field(() => Date, { nullable: true }) + expirationDate?: Date + + @Field({ nullable: true }) + credentialID?: string + + @Field({ nullable: true }) + credentialURL?: string + + @Field(() => GraphQLUpload, { nullable: true }) + photo?: Promise +} \ No newline at end of file diff --git a/src/graphql-types/QualificationResponse.ts b/src/graphql-types/QualificationResponse.ts new file mode 100644 index 0000000..1ac4050 --- /dev/null +++ b/src/graphql-types/QualificationResponse.ts @@ -0,0 +1,12 @@ +import { Qualification } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class QualificationResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Qualification, { nullable: true }) + qualification?: Qualification +} \ No newline at end of file diff --git a/src/graphql-types/SpaceInfo.ts b/src/graphql-types/SpaceInfo.ts new file mode 100644 index 0000000..d022321 --- /dev/null +++ b/src/graphql-types/SpaceInfo.ts @@ -0,0 +1,16 @@ +import { Field, InputType } from 'type-graphql' + +@InputType() +export class SpaceInfo { + @Field({ nullable: true }) + fullSpaceName?: string + + @Field({ nullable: true }) + headline?: string + + @Field({ nullable: true }) + rules?: string + + @Field({ nullable: true }) + about?: string +} \ No newline at end of file diff --git a/src/graphql-types/SpaceResponse.ts b/src/graphql-types/SpaceResponse.ts new file mode 100644 index 0000000..042747b --- /dev/null +++ b/src/graphql-types/SpaceResponse.ts @@ -0,0 +1,12 @@ +import { Space } from '@entities' +import { Field, ObjectType } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() +export class SpaceResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => Space, { nullable: true }) + space?: Space +} \ No newline at end of file diff --git a/src/graphql-types/UploadedFileResponse.ts b/src/graphql-types/UploadedFileResponse.ts new file mode 100644 index 0000000..e7f8f07 --- /dev/null +++ b/src/graphql-types/UploadedFileResponse.ts @@ -0,0 +1,16 @@ +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +export class UploadedFileResponse { + @Field() + filename!: String + + @Field() + mimetype!: String + + @Field() + encoding!: String + + @Field() + url!: String +} \ No newline at end of file diff --git a/src/graphql-types/UserInfo.ts b/src/graphql-types/UserInfo.ts new file mode 100644 index 0000000..fb80ae0 --- /dev/null +++ b/src/graphql-types/UserInfo.ts @@ -0,0 +1,16 @@ +import { Field, InputType } from 'type-graphql' + +@InputType() +export class UserInfo { + @Field({ nullable: true }) + fullName?: string + + @Field({ nullable: true }) + headline?: string + + @Field({ nullable: true }) + address?: string + + @Field({ nullable: true }) + about?: string +} \ No newline at end of file diff --git a/src/graphql-types/UserResponse.ts b/src/graphql-types/UserResponse.ts new file mode 100644 index 0000000..f97b3ad --- /dev/null +++ b/src/graphql-types/UserResponse.ts @@ -0,0 +1,12 @@ +import { User } from '../entities' +import { ObjectType, Field } from 'type-graphql' +import { FieldError } from './FieldError' + +@ObjectType() // ObjectType is for return values +export class UserResponse { + @Field(() => [FieldError], { nullable: true }) + errors?: FieldError[] + + @Field(() => User, { nullable: true }) + user?: User +} \ No newline at end of file diff --git a/src/graphql-types/index.ts b/src/graphql-types/index.ts new file mode 100644 index 0000000..53a53f4 --- /dev/null +++ b/src/graphql-types/index.ts @@ -0,0 +1,27 @@ +export * from './FieldError' +export * from './UserResponse' +export * from './ForgotPasswordResponse' +export * from './PaginatedPosts' +export * from './PostInput' +export * from './PostResponse' +export * from './PaginatedComments' +export * from './OfferResponse' +export * from './OfferInput' +export * from './PaginatedOffers' +export * from './UploadedFileResponse' +export * from './MessageInput' +export * from './PhotoResponse' +export * from './PageResponse' +export * from './SpaceResponse' +export * from './UserInfo' +export * from './EducationItemResponse' +export * from './EducationItemInput' +export * from './ExperienceInput' +export * from './ExperienceResponse' +export * from './QualificationInput' +export * from './QualificationResponse' +export * from './PageInfo' +export * from './SpaceInfo' +export * from './InboxResponse' +export * from './CreateConversationReponse' +export * from './Location' \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..93dd457 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,140 @@ +import { __prod__, COOKIE_NAME } from '@constants' +import { createApplicantsNoLoader, createApplicationLoader, createApplicationStatusLoader, createCommentVoteLoader, createFollowerNumberLoader, createFollowLoader, createModStatusLoader, createOwnerStatusLoader, createPageLoader, createSpaceLoader, createSubscriberNumberLoader, createSubscriptionLoader, createTagLoader, createUserFollowerNumberLoader, createUserFollowingNumberLoader, createUserFollowLoader, createUserLoader, createVoteLoader } from '@loaders' +import { CommentResolver, ConversationResolver, CVResolver, EducationItemResolver, ExperienceResolver, OfferResolver, PageResolver, PostResolver, QualificationResolver, QuicksightResolver, S3Resolver, SpaceResolver, TagResolver, UserResolver } from '@resolvers' +import typeORMSource from '@src/type-orm.source' +import { Context } from '@types' +import { ApolloServer } from 'apollo-server-express' +import connectRedis from 'connect-redis' +import cors from 'cors' +import express from 'express' +import session from 'express-session' +import { graphqlUploadExpress } from 'graphql-upload' +import Redis from 'ioredis' +import { buildSchema } from 'type-graphql' + +const main = async () => { + const orm = await typeORMSource.initialize() + // await orm.dropDatabase() + await orm.runMigrations() + + const app = express() + app.options('*', cors({ + credentials: true, + origin: process.env.FRONTEND_ORIGIN, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization'] + })) + app.use(cors({ + credentials: true, + origin: process.env.FRONTEND_ORIGIN, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization'] + })) + app.use(express.json({ + limit: '1gb', + verify: (_, __, buf) => { + if (buf.byteLength / (1024 * 1024) > 1024) { + throw new Error(`${process.env.NON_API_ERROR_PREFIX}Request too large. It must be less than 1GB.${process.env.NON_API_ERROR_SUFFIX}`) + } + } + })) + + const RedisStore = connectRedis(session) + const redis = new Redis(process.env.REDIS_URL) + // await redis.flushall() + + app.set('trust proxy', true) + + app.use( + session({ + name: COOKIE_NAME, + store: new RedisStore({ + client: redis, + disableTTL: true, + disableTouch: true + }), + cookie: { // lax + secure false -> chrome dev; none + secure true -> apollo studio, prod: lax + secure + maxAge: 1000 * 60 * 60 * 24 * 365 * 10, + httpOnly: false, + sameSite: 'lax', + // sameSite: 'none', + secure: true, + // secure: false, + // domain: __prod__ ? process.env.DOMAIN_SUFFIX : undefined + domain: process.env.DOMAIN_SUFFIX + }, + secret: process.env.REDIS_SECRET, + saveUninitialized: false, + resave: false + }) + ) + + app.listen(parseInt(process.env.API_PORT), () => { + if (!__prod__) { + console.log(`Server started on localhost:${process.env.API_PORT}.`) + } + else { + console.log(`Server started at ${process.env.BACKEND_ORIGIN}.`) + } + }) + app.get('/', (_, res) => { + res.send('Start querying at /graphql.') + }) + + const apolloServer = new ApolloServer({ + introspection: true, + schema: await buildSchema({ + resolvers: [ + UserResolver, + PostResolver, + SpaceResolver, + CommentResolver, + OfferResolver, + TagResolver, + S3Resolver, + ConversationResolver, + PageResolver, + EducationItemResolver, + ExperienceResolver, + QualificationResolver, + CVResolver, + QuicksightResolver + ], + validate: false + }), + + context: ({ req, res }): Context => ({ + orm, + req, + res, + redis, + userLoader: createUserLoader(), + voteLoader: createVoteLoader(), + spaceLoader: createSpaceLoader(), + commentVoteLoader: createCommentVoteLoader(), + tagLoader: createTagLoader(), + subscriptionLoader: createSubscriptionLoader(), + modStatusLoader: createModStatusLoader(), + pageLoader: createPageLoader(), + followLoader: createFollowLoader(), + ownerStatusLoader: createOwnerStatusLoader(), + userFollowLoader: createUserFollowLoader(), + applicationStatusLoader: createApplicationStatusLoader(), + applicationLoader: createApplicationLoader(), + applicantsNoLoader: createApplicantsNoLoader(), + followerNumberLoader: createFollowerNumberLoader(), + subscriberNumberLoader: createSubscriberNumberLoader(), + userFollowerNumberLoader: createUserFollowerNumberLoader(), + userFollowingNumberLoader: createUserFollowingNumberLoader() + }) + }) + + await apolloServer.start() + apolloServer.applyMiddleware({ + app, + cors: false + }) + app.use(graphqlUploadExpress()) +} + +main().catch(console.error) \ No newline at end of file diff --git a/src/loaders/createApplicantsNoLoader.ts b/src/loaders/createApplicantsNoLoader.ts new file mode 100644 index 0000000..89087a1 --- /dev/null +++ b/src/loaders/createApplicantsNoLoader.ts @@ -0,0 +1,20 @@ +import { OfferApplication } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createApplicantsNoLoader = () => + new DataLoader(async (offerIds) => { + const result: { count: string, offerId: number }[] = await orm.createQueryBuilder() + .select('COUNT(*), "offerId"') + .from(OfferApplication, 'oa') + .orWhere(offerIds.map(id => ({ offerId: id }))) + .groupBy('"offerId"') + .execute() + return offerIds.map(id => { + const count = result.find(r => r.offerId == id)?.count + if (count) { + return parseInt(count) + } + return 0 + }) + }) \ No newline at end of file diff --git a/src/loaders/createApplicationLoader.ts b/src/loaders/createApplicationLoader.ts new file mode 100644 index 0000000..5e3ec6d --- /dev/null +++ b/src/loaders/createApplicationLoader.ts @@ -0,0 +1,11 @@ +import { OfferApplication } from '@entities' +import DataLoader from 'dataloader' +import { In } from 'typeorm' + +export const createApplicationLoader = () => + new DataLoader(async (offerIds) => { + const applications = await OfferApplication.find({ where: { offerId: In(offerIds as number[]) }, relations: { user: true } }) + return offerIds.map(offerId => { + return applications.filter(application => application.offerId === offerId) + }) + }) \ No newline at end of file diff --git a/src/loaders/createApplicationStatusLoader.ts b/src/loaders/createApplicationStatusLoader.ts new file mode 100644 index 0000000..ae1cbcf --- /dev/null +++ b/src/loaders/createApplicationStatusLoader.ts @@ -0,0 +1,15 @@ +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createApplicationStatusLoader = () => + new DataLoader<{ offerId: number, userId: number }, String | null>(async (keys) => { + const applications: { id: number, offerId: number, userId: number, status: string }[] = await orm.createQueryBuilder() + .select('*') + .from('offer_application', 'oa') + .orWhere(keys) + .execute() + return keys.map(key => { + const application = applications.find(a => a.offerId == key.offerId && a.userId == key.userId) + return application ? application.status : null + }) + }) \ No newline at end of file diff --git a/src/loaders/createCommentVoteLoader.ts b/src/loaders/createCommentVoteLoader.ts new file mode 100644 index 0000000..5d37119 --- /dev/null +++ b/src/loaders/createCommentVoteLoader.ts @@ -0,0 +1,18 @@ +import { CommentVote } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createCommentVoteLoader = () => + + new DataLoader<{ commentId: number, userId: number }, CommentVote | null>(async (keys) => { + const commentVotes: CommentVote[] = await orm.createQueryBuilder() + .select('*') + .from(CommentVote, 'cv') + .orWhereInIds(keys) + .execute() + const map: Record = {} + commentVotes.forEach(cv => { + map[`${cv.userId}-${cv.commentId}`] = cv + }) + return keys.map(key => map[`${key.userId}-${key.commentId}`]) + }) \ No newline at end of file diff --git a/src/loaders/createFollowLoader.ts b/src/loaders/createFollowLoader.ts new file mode 100644 index 0000000..90a8af7 --- /dev/null +++ b/src/loaders/createFollowLoader.ts @@ -0,0 +1,16 @@ +import { PageFollow } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createFollowLoader = () => + new DataLoader<{ pageId: number, userId: number }, boolean>(async (keys) => { + const follows: PageFollow[] = await orm.createQueryBuilder() + .select('*') + .from(PageFollow, 'pf') + .orWhereInIds(keys) + .execute() + return keys.map(key => { + const follow = follows.find(fol => fol.pageId == key.pageId && fol.userId == key.userId) + return follow ? true : false + }) + }) \ No newline at end of file diff --git a/src/loaders/createFollowerNumberLoader.ts b/src/loaders/createFollowerNumberLoader.ts new file mode 100644 index 0000000..b682066 --- /dev/null +++ b/src/loaders/createFollowerNumberLoader.ts @@ -0,0 +1,20 @@ +import { PageFollow } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createFollowerNumberLoader = () => + new DataLoader(async (pageIds) => { + const result: { count: string, pageId: number }[] = await orm.createQueryBuilder() + .select('COUNT(*), "pageId"') + .from(PageFollow, 'pf') + .orWhere(pageIds.map(id => ({ pageId: id }))) + .groupBy('"pageId"') + .execute() + return pageIds.map(id => { + const count = result.find(r => r.pageId == id)?.count + if (count) { + return parseInt(count) + } + return 0 + }) + }) \ No newline at end of file diff --git a/src/loaders/createModStatusLoader.ts b/src/loaders/createModStatusLoader.ts new file mode 100644 index 0000000..afc3b77 --- /dev/null +++ b/src/loaders/createModStatusLoader.ts @@ -0,0 +1,16 @@ +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createModStatusLoader = () => + new DataLoader<{ spaceId: number, userId: number }, boolean>(async (keys) => { + const modProofs: { spaceId: number, userId: number }[] = await orm.createQueryBuilder() + .select('*') + .from('space_mods_user', 'smu') + .orWhereInIds(keys) + .execute() + + return keys.map(key => { + const proof = modProofs.find(mp => mp.spaceId == key.spaceId && mp.userId == key.userId) + return proof ? true : false + }) + }) \ No newline at end of file diff --git a/src/loaders/createOwnerStatusLoader.ts b/src/loaders/createOwnerStatusLoader.ts new file mode 100644 index 0000000..cdfc473 --- /dev/null +++ b/src/loaders/createOwnerStatusLoader.ts @@ -0,0 +1,15 @@ +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createOwnerStatusLoader = () => + new DataLoader<{ pageId: number, userId: number }, boolean>(async (keys) => { + const ownerProofs: { pageId: number, userId: number }[] = await orm.createQueryBuilder() + .select('*') + .from('page_owners_user', 'pou') + .orWhereInIds(keys) + .execute() + return keys.map(key => { + const ownerProof = ownerProofs.find(o => o.pageId == key.pageId && o.userId == key.userId) + return ownerProof ? true : false + }) + }) \ No newline at end of file diff --git a/src/loaders/createPageLoader.ts b/src/loaders/createPageLoader.ts new file mode 100644 index 0000000..a7fa483 --- /dev/null +++ b/src/loaders/createPageLoader.ts @@ -0,0 +1,15 @@ +import DataLoader from 'dataloader' +import { In } from 'typeorm' +import { Page, User } from '@entities' + +export const createPageLoader = () => + + new DataLoader(async (pageIds) => { + const pages = await Page.findBy({ id: In(pageIds as number[]) }) + + const map: Record = {} + pages.forEach(p => { + map[p.id] = p + }) + return pageIds.map(id => map[id]) + }) \ No newline at end of file diff --git a/src/loaders/createSpaceLoader.ts b/src/loaders/createSpaceLoader.ts new file mode 100644 index 0000000..eb57495 --- /dev/null +++ b/src/loaders/createSpaceLoader.ts @@ -0,0 +1,15 @@ +import DataLoader from 'dataloader' +import { In } from 'typeorm' +import { Space } from '@entities' + +export const createSpaceLoader = () => + + new DataLoader(async (spaceIds) => { + const spaces = await Space.findBy({ id: In(spaceIds as number[]) }) + + const map: Record = {} + spaces.forEach(s => { + map[s.id] = s + }) + return spaceIds.map(id => map[id]) + }) \ No newline at end of file diff --git a/src/loaders/createSubscriberNumberLoader.ts b/src/loaders/createSubscriberNumberLoader.ts new file mode 100644 index 0000000..4356834 --- /dev/null +++ b/src/loaders/createSubscriberNumberLoader.ts @@ -0,0 +1,20 @@ +import { SpaceSubscription } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createSubscriberNumberLoader = () => + new DataLoader(async (spaceIds) => { + const result: { count: string, spaceId: number }[] = await orm.createQueryBuilder() + .select('COUNT(*), "spaceId"') + .from(SpaceSubscription, 'ss') + .orWhere(spaceIds.map(id => ({ spaceId: id }))) + .groupBy('"spaceId"') + .execute() + return spaceIds.map(id => { + const count = result.find(r => r.spaceId == id)?.count + if (count) { + return parseInt(count) + } + return 0 + }) + }) \ No newline at end of file diff --git a/src/loaders/createSubscriptionLoader.ts b/src/loaders/createSubscriptionLoader.ts new file mode 100644 index 0000000..c23267b --- /dev/null +++ b/src/loaders/createSubscriptionLoader.ts @@ -0,0 +1,16 @@ +import { SpaceSubscription } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createSubscriptionLoader = () => + new DataLoader<{ spaceId: number, userId: number }, boolean>(async (keys) => { + const subscriptions: SpaceSubscription[] = await orm.createQueryBuilder() + .select('*') + .from(SpaceSubscription, 'ss') + .orWhereInIds(keys) + .execute() + return keys.map(key => { + const subscription = subscriptions.find(sub => sub.spaceId == key.spaceId && sub.userId == key.userId) + return subscription ? true : false + }) + }) \ No newline at end of file diff --git a/src/loaders/createTagLoader.ts b/src/loaders/createTagLoader.ts new file mode 100644 index 0000000..a8842b7 --- /dev/null +++ b/src/loaders/createTagLoader.ts @@ -0,0 +1,27 @@ +import { Tag } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createTagLoader = () => + + new DataLoader(async (postIds) => { + const tagItems: { postId: number, tagId: number }[] = await orm.query(` + SELECT * FROM post_tags_tag WHERE "postId" IN (${postIds.join(', ')}); + `) + const tagIds = tagItems.map(ti => ti.tagId) + if (tagIds.length > 0) { + const tags: Tag[] = await orm.query(` + SELECT * FROM tag WHERE id IN (${tagIds.join(', ')}); + `) + + return postIds.map(id => { + const postTagItems = tagItems.filter(ti => ti.postId == id) + const postTagIds = postTagItems.map(pti => pti.tagId) + return tags.filter(t => postTagIds.includes(t.id)) + }) + } + else { + const newArr = postIds.map(id => []) + return newArr + } + }) \ No newline at end of file diff --git a/src/loaders/createUserFollowLoader.ts b/src/loaders/createUserFollowLoader.ts new file mode 100644 index 0000000..95713be --- /dev/null +++ b/src/loaders/createUserFollowLoader.ts @@ -0,0 +1,16 @@ +import { UserFollow } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createUserFollowLoader = () => + new DataLoader<{ followedUserId: number, followingUserId: number }, boolean>(async (keys) => { + const follows: UserFollow[] = await orm.createQueryBuilder() + .select('*') + .from(UserFollow, 'uf') + .orWhereInIds(keys) + .execute() + return keys.map(key => { + const follow = follows.find(fol => fol.followedUserId == key.followedUserId && fol.followingUserId == key.followingUserId) + return follow ? true : false + }) + }) \ No newline at end of file diff --git a/src/loaders/createUserFollowerNumberLoader.ts b/src/loaders/createUserFollowerNumberLoader.ts new file mode 100644 index 0000000..2429b13 --- /dev/null +++ b/src/loaders/createUserFollowerNumberLoader.ts @@ -0,0 +1,20 @@ +import { UserFollow } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createUserFollowerNumberLoader = () => + new DataLoader(async (userIds) => { + const result: { count: string, followedUserId: number }[] = await orm.createQueryBuilder() + .select('COUNT(*), "followedUserId"') + .from(UserFollow, 'uf') + .orWhere(userIds.map(id => ({ followedUserId: id }))) + .groupBy('"followedUserId"') + .execute() + return userIds.map(id => { + const count = result.find(r => r.followedUserId == id)?.count + if (count) { + return parseInt(count) + } + return 0 + }) + }) \ No newline at end of file diff --git a/src/loaders/createUserFollowingNumberLoader.ts b/src/loaders/createUserFollowingNumberLoader.ts new file mode 100644 index 0000000..9e5486c --- /dev/null +++ b/src/loaders/createUserFollowingNumberLoader.ts @@ -0,0 +1,20 @@ +import { UserFollow } from '@entities' +import orm from '@src/type-orm.source' +import DataLoader from 'dataloader' + +export const createUserFollowingNumberLoader = () => + new DataLoader(async (userIds) => { + const result: { count: string, followingUserId: number }[] = await orm.createQueryBuilder() + .select('COUNT(*), "followingUserId"') + .from(UserFollow, 'uf') + .orWhere(userIds.map(id => ({ followingUserId: id }))) + .groupBy('"followingUserId"') + .execute() + return userIds.map(id => { + const count = result.find(r => r.followingUserId == id)?.count + if (count) { + return parseInt(count) + } + return 0 + }) + }) \ No newline at end of file diff --git a/src/loaders/createUserLoader.ts b/src/loaders/createUserLoader.ts new file mode 100644 index 0000000..ff7ef6d --- /dev/null +++ b/src/loaders/createUserLoader.ts @@ -0,0 +1,15 @@ +import DataLoader from 'dataloader' +import { In } from 'typeorm' +import { User } from '@entities' + +export const createUserLoader = () => + + new DataLoader(async (userIds) => { + const users = await User.findBy({ id: In(userIds as number[]) }) + + const map: Record = {} + users.forEach(u => { + map[u.id] = u + }) + return userIds.map(id => map[id]) + }) \ No newline at end of file diff --git a/src/loaders/createVoteLoader.ts b/src/loaders/createVoteLoader.ts new file mode 100644 index 0000000..bba01c2 --- /dev/null +++ b/src/loaders/createVoteLoader.ts @@ -0,0 +1,18 @@ +import DataLoader from 'dataloader' +import { Vote } from '@entities' +import orm from '@src/type-orm.source' + +export const createVoteLoader = () => + + new DataLoader<{ postId: number, userId: number }, Vote | null>(async (keys) => { + const votes: Vote[] = await orm.createQueryBuilder() + .select('*') + .from(Vote, 'v') + .orWhereInIds(keys) + .execute() + const map: Record = {} + votes.forEach(v => { + map[`${v.userId}-${v.postId}`] = v + }) + return keys.map(key => map[`${key.userId}-${key.postId}`]) + }) \ No newline at end of file diff --git a/src/loaders/index.ts b/src/loaders/index.ts new file mode 100644 index 0000000..1e59150 --- /dev/null +++ b/src/loaders/index.ts @@ -0,0 +1,18 @@ +export * from './createUserLoader' +export * from './createVoteLoader' +export * from './createSpaceLoader' +export * from './createCommentVoteLoader' +export * from './createTagLoader' +export * from './createSubscriptionLoader' +export * from './createModStatusLoader' +export * from './createPageLoader' +export * from './createFollowLoader' +export * from './createOwnerStatusLoader' +export * from './createUserFollowLoader' +export * from './createApplicationStatusLoader' +export * from './createApplicationLoader' +export * from './createApplicantsNoLoader' +export * from './createFollowerNumberLoader' +export * from './createSubscriberNumberLoader' +export * from './createUserFollowerNumberLoader' +export * from './createUserFollowingNumberLoader' \ No newline at end of file diff --git a/src/middlewares/index.ts b/src/middlewares/index.ts new file mode 100644 index 0000000..e9cfb77 --- /dev/null +++ b/src/middlewares/index.ts @@ -0,0 +1 @@ +export * from './isAuth' \ No newline at end of file diff --git a/src/middlewares/isAuth.ts b/src/middlewares/isAuth.ts new file mode 100644 index 0000000..7e30f89 --- /dev/null +++ b/src/middlewares/isAuth.ts @@ -0,0 +1,12 @@ +import { Context } from '@types' +import { MiddlewareFn } from 'type-graphql' +import { User } from '@entities' + +export const isAuth: MiddlewareFn = async ({ context }, next) => { + if (!context.req.session.userId || + !(await User.findOne({ where: { id: context.req.session.userId } })) + ) { + throw new Error('Not authenticated.') + } + await next() +} \ No newline at end of file diff --git a/src/migrations/1660494342130-Initial.ts b/src/migrations/1660494342130-Initial.ts new file mode 100644 index 0000000..f1e09a5 --- /dev/null +++ b/src/migrations/1660494342130-Initial.ts @@ -0,0 +1,128 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Initial1660494342130 implements MigrationInterface { + name = 'Initial1660494342130' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "username" character varying NOT NULL, "email" character varying NOT NULL, "password" character varying NOT NULL, "skills" text, "avatar" character varying, "coverPhoto" character varying, "fullName" character varying, "headline" character varying, "address" character varying, "about" character varying, "mostRecentLatitude" double precision, "mostRecentLongitude" double precision, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "post" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "title" character varying NOT NULL, "text" character varying NOT NULL, "creatorType" character varying NOT NULL DEFAULT 'user', "creatorId" integer, "pageCreatorId" integer, "points" integer NOT NULL DEFAULT '0', "spaceId" integer NOT NULL, CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "vote" ("value" integer NOT NULL, "userId" integer NOT NULL, "postId" integer NOT NULL, CONSTRAINT "PK_16e301aa5efdd994626b2635186" PRIMARY KEY ("userId", "postId"))`); + await queryRunner.query(`CREATE TABLE "space" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "spaceName" character varying NOT NULL, "avatar" character varying, "coverPhoto" character varying, "fullSpaceName" character varying, "rules" character varying, "headline" character varying, "about" character varying, CONSTRAINT "UQ_d3a1749188980a055b0bcd4321d" UNIQUE ("spaceName"), CONSTRAINT "PK_094f5ec727fe052956a11623640" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "space_subscription" ("userId" integer NOT NULL, "spaceId" integer NOT NULL, CONSTRAINT "PK_d628d32fd2777438e93174bc72a" PRIMARY KEY ("userId", "spaceId"))`); + await queryRunner.query(`CREATE TABLE "comment" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "text" character varying NOT NULL, "creatorType" character varying NOT NULL DEFAULT 'user', "creatorId" integer, "pageCreatorId" integer, "postId" integer NOT NULL, "points" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_0b0e4bbc8415ec426f87f3a88e2" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "comment_vote" ("value" integer NOT NULL, "userId" integer NOT NULL, "commentId" integer NOT NULL, CONSTRAINT "PK_9194f426d41fb9a8abc3aae5114" PRIMARY KEY ("userId", "commentId"))`); + await queryRunner.query(`CREATE TABLE "offer_application" ("id" SERIAL NOT NULL, "userId" integer NOT NULL, "offerId" integer NOT NULL, "status" character varying NOT NULL DEFAULT 'applied', CONSTRAINT "PK_d8989a17a7c87cfa5ff63ea84c1" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "offer" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "creatorType" character varying NOT NULL DEFAULT 'user', "creatorId" integer, "pageCreatorId" integer, "spaceId" integer NOT NULL, "title" character varying NOT NULL, "workplace" character varying, "address" character varying, "recruiting" boolean NOT NULL DEFAULT false, "employmentType" character varying, "salaryRange" character varying, "department" character varying, "requirements" character varying, "benefits" character varying, "description" character varying, "photo" character varying, CONSTRAINT "PK_57c6ae1abe49201919ef68de900" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "tag" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_8e4052373c579afc1471f526760" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "conversation" ("id" SERIAL NOT NULL, "participants" integer array NOT NULL, "firestoreCollectionId" character varying NOT NULL, CONSTRAINT "PK_864528ec4274360a40f66c29845" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "page" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "pageName" character varying NOT NULL, "email" character varying, "avatar" character varying, "coverPhoto" character varying, "fullPageName" character varying, "headline" character varying, "address" character varying, "about" character varying, CONSTRAINT "UQ_37f114be85015bdfea6ecfefe6d" UNIQUE ("pageName"), CONSTRAINT "UQ_add9b61d89ec1480843a61c924d" UNIQUE ("email"), CONSTRAINT "PK_742f4117e065c5b6ad21b37ba1f" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "page_follow" ("userId" integer NOT NULL, "pageId" integer NOT NULL, CONSTRAINT "PK_ac8f7df2a892662f4edf458ca33" PRIMARY KEY ("userId", "pageId"))`); + await queryRunner.query(`CREATE TABLE "user_follow" ("followingUserId" integer NOT NULL, "followedUserId" integer NOT NULL, CONSTRAINT "PK_bdb4b69fec91b1b2deb7df00b5e" PRIMARY KEY ("followingUserId", "followedUserId"))`); + await queryRunner.query(`CREATE TABLE "education_item" ("id" SERIAL NOT NULL, "school" character varying NOT NULL, "status" character varying, "startDate" TIMESTAMP WITH TIME ZONE, "endDate" TIMESTAMP WITH TIME ZONE, "userId" integer, "photo" character varying, CONSTRAINT "PK_115272e55085398f5a282496bb8" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "experience" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "workplace" character varying, "startDate" TIMESTAMP WITH TIME ZONE, "endDate" TIMESTAMP WITH TIME ZONE, "userId" integer, "photo" character varying, CONSTRAINT "PK_5e8d5a534100e1b17ee2efa429a" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "qualification" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "issuingOrganisation" character varying NOT NULL, "issuanceDate" TIMESTAMP WITH TIME ZONE, "expire" boolean NOT NULL, "expirationDate" TIMESTAMP WITH TIME ZONE, "credentialID" character varying, "credentialURL" character varying, "userId" integer, "photo" character varying, CONSTRAINT "PK_c8244868552c4364a5264440a66" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "cv" ("id" SERIAL NOT NULL, "filename" character varying NOT NULL, "key" character varying NOT NULL, "userId" integer, CONSTRAINT "PK_4ddf7891daf83c3506efa503bb8" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "post_tags_tag" ("postId" integer NOT NULL, "tagId" integer NOT NULL, CONSTRAINT "PK_e9b7b8e6a07bdccb6a954171676" PRIMARY KEY ("postId", "tagId"))`); + await queryRunner.query(`CREATE INDEX "IDX_b651178cc41334544a7a9601c4" ON "post_tags_tag" ("postId") `); + await queryRunner.query(`CREATE INDEX "IDX_41e7626b9cc03c5c65812ae55e" ON "post_tags_tag" ("tagId") `); + await queryRunner.query(`CREATE TABLE "space_mods_user" ("spaceId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_4768be7da937c7ee02c260fb761" PRIMARY KEY ("spaceId", "userId"))`); + await queryRunner.query(`CREATE INDEX "IDX_6fac656f12453c24f08855fbef" ON "space_mods_user" ("spaceId") `); + await queryRunner.query(`CREATE INDEX "IDX_2172d469a5e9c5e4397bcb94ff" ON "space_mods_user" ("userId") `); + await queryRunner.query(`CREATE TABLE "page_owners_user" ("pageId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_2b7f0419bfbb8f0550592d2cf37" PRIMARY KEY ("pageId", "userId"))`); + await queryRunner.query(`CREATE INDEX "IDX_1c5a8df532d6c129c4f1642b2f" ON "page_owners_user" ("pageId") `); + await queryRunner.query(`CREATE INDEX "IDX_5bd7deaf0c8e15ea861d15efc3" ON "page_owners_user" ("userId") `); + await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_9e91e6a24261b66f53971d3f96b" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_605fe7290cfa226a4d444c05ca4" FOREIGN KEY ("pageCreatorId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_899a1931431768b2800f695ac60" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "vote" ADD CONSTRAINT "FK_f5de237a438d298031d11a57c3b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "vote" ADD CONSTRAINT "FK_43cc1af57676ac1b7ec774bd10f" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "space_subscription" ADD CONSTRAINT "FK_9f89ed79bb85a199306ce7e3ae1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "space_subscription" ADD CONSTRAINT "FK_4c2cf829c698bede8b9c8608f10" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "comment" ADD CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "comment" ADD CONSTRAINT "FK_37de0ca7e7c8dd9f6a3e1f2b6d3" FOREIGN KEY ("pageCreatorId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "comment" ADD CONSTRAINT "FK_94a85bb16d24033a2afdd5df060" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "comment_vote" ADD CONSTRAINT "FK_ade7498b89296b9fb63bcd8dbdd" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "comment_vote" ADD CONSTRAINT "FK_5d77d92a6925ae3fc8da14e1257" FOREIGN KEY ("commentId") REFERENCES "comment"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "offer_application" ADD CONSTRAINT "FK_dcfd5b26f80494128baf7bb7578" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "offer_application" ADD CONSTRAINT "FK_6cb6ed00606a2fb71c05fb3c871" FOREIGN KEY ("offerId") REFERENCES "offer"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_cd84aed04fbd70e9ad48139c727" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_f4621bc66549eda711a0b7c6558" FOREIGN KEY ("pageCreatorId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_69bbcc303da6f7da97744be788a" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "page_follow" ADD CONSTRAINT "FK_732300fd551f8b08e267018ed99" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "page_follow" ADD CONSTRAINT "FK_b0cd715ee8609422e661e0c43fc" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "user_follow" ADD CONSTRAINT "FK_90ab1c414dac6701f4c126fb736" FOREIGN KEY ("followingUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "user_follow" ADD CONSTRAINT "FK_4e493e20c9c272183e73da57ddf" FOREIGN KEY ("followedUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "education_item" ADD CONSTRAINT "FK_1dd0c4d5f3e2500b08c3d137b29" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "experience" ADD CONSTRAINT "FK_cbfb1d1219454c9b45f1b3c4274" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "qualification" ADD CONSTRAINT "FK_6550f632c00a2739a1dad0381c1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "cv" ADD CONSTRAINT "FK_e4b7330e64fd0ecce86720e62f9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "post_tags_tag" ADD CONSTRAINT "FK_b651178cc41334544a7a9601c45" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "post_tags_tag" ADD CONSTRAINT "FK_41e7626b9cc03c5c65812ae55e8" FOREIGN KEY ("tagId") REFERENCES "tag"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "space_mods_user" ADD CONSTRAINT "FK_6fac656f12453c24f08855fbefc" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "space_mods_user" ADD CONSTRAINT "FK_2172d469a5e9c5e4397bcb94ff2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "page_owners_user" ADD CONSTRAINT "FK_1c5a8df532d6c129c4f1642b2ff" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "page_owners_user" ADD CONSTRAINT "FK_5bd7deaf0c8e15ea861d15efc36" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "page_owners_user" DROP CONSTRAINT "FK_5bd7deaf0c8e15ea861d15efc36"`); + await queryRunner.query(`ALTER TABLE "page_owners_user" DROP CONSTRAINT "FK_1c5a8df532d6c129c4f1642b2ff"`); + await queryRunner.query(`ALTER TABLE "space_mods_user" DROP CONSTRAINT "FK_2172d469a5e9c5e4397bcb94ff2"`); + await queryRunner.query(`ALTER TABLE "space_mods_user" DROP CONSTRAINT "FK_6fac656f12453c24f08855fbefc"`); + await queryRunner.query(`ALTER TABLE "post_tags_tag" DROP CONSTRAINT "FK_41e7626b9cc03c5c65812ae55e8"`); + await queryRunner.query(`ALTER TABLE "post_tags_tag" DROP CONSTRAINT "FK_b651178cc41334544a7a9601c45"`); + await queryRunner.query(`ALTER TABLE "cv" DROP CONSTRAINT "FK_e4b7330e64fd0ecce86720e62f9"`); + await queryRunner.query(`ALTER TABLE "qualification" DROP CONSTRAINT "FK_6550f632c00a2739a1dad0381c1"`); + await queryRunner.query(`ALTER TABLE "experience" DROP CONSTRAINT "FK_cbfb1d1219454c9b45f1b3c4274"`); + await queryRunner.query(`ALTER TABLE "education_item" DROP CONSTRAINT "FK_1dd0c4d5f3e2500b08c3d137b29"`); + await queryRunner.query(`ALTER TABLE "user_follow" DROP CONSTRAINT "FK_4e493e20c9c272183e73da57ddf"`); + await queryRunner.query(`ALTER TABLE "user_follow" DROP CONSTRAINT "FK_90ab1c414dac6701f4c126fb736"`); + await queryRunner.query(`ALTER TABLE "page_follow" DROP CONSTRAINT "FK_b0cd715ee8609422e661e0c43fc"`); + await queryRunner.query(`ALTER TABLE "page_follow" DROP CONSTRAINT "FK_732300fd551f8b08e267018ed99"`); + await queryRunner.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_69bbcc303da6f7da97744be788a"`); + await queryRunner.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_f4621bc66549eda711a0b7c6558"`); + await queryRunner.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_cd84aed04fbd70e9ad48139c727"`); + await queryRunner.query(`ALTER TABLE "offer_application" DROP CONSTRAINT "FK_6cb6ed00606a2fb71c05fb3c871"`); + await queryRunner.query(`ALTER TABLE "offer_application" DROP CONSTRAINT "FK_dcfd5b26f80494128baf7bb7578"`); + await queryRunner.query(`ALTER TABLE "comment_vote" DROP CONSTRAINT "FK_5d77d92a6925ae3fc8da14e1257"`); + await queryRunner.query(`ALTER TABLE "comment_vote" DROP CONSTRAINT "FK_ade7498b89296b9fb63bcd8dbdd"`); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_94a85bb16d24033a2afdd5df060"`); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_37de0ca7e7c8dd9f6a3e1f2b6d3"`); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f"`); + await queryRunner.query(`ALTER TABLE "space_subscription" DROP CONSTRAINT "FK_4c2cf829c698bede8b9c8608f10"`); + await queryRunner.query(`ALTER TABLE "space_subscription" DROP CONSTRAINT "FK_9f89ed79bb85a199306ce7e3ae1"`); + await queryRunner.query(`ALTER TABLE "vote" DROP CONSTRAINT "FK_43cc1af57676ac1b7ec774bd10f"`); + await queryRunner.query(`ALTER TABLE "vote" DROP CONSTRAINT "FK_f5de237a438d298031d11a57c3b"`); + await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_899a1931431768b2800f695ac60"`); + await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_605fe7290cfa226a4d444c05ca4"`); + await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_9e91e6a24261b66f53971d3f96b"`); + await queryRunner.query(`DROP INDEX "public"."IDX_5bd7deaf0c8e15ea861d15efc3"`); + await queryRunner.query(`DROP INDEX "public"."IDX_1c5a8df532d6c129c4f1642b2f"`); + await queryRunner.query(`DROP TABLE "page_owners_user"`); + await queryRunner.query(`DROP INDEX "public"."IDX_2172d469a5e9c5e4397bcb94ff"`); + await queryRunner.query(`DROP INDEX "public"."IDX_6fac656f12453c24f08855fbef"`); + await queryRunner.query(`DROP TABLE "space_mods_user"`); + await queryRunner.query(`DROP INDEX "public"."IDX_41e7626b9cc03c5c65812ae55e"`); + await queryRunner.query(`DROP INDEX "public"."IDX_b651178cc41334544a7a9601c4"`); + await queryRunner.query(`DROP TABLE "post_tags_tag"`); + await queryRunner.query(`DROP TABLE "cv"`); + await queryRunner.query(`DROP TABLE "qualification"`); + await queryRunner.query(`DROP TABLE "experience"`); + await queryRunner.query(`DROP TABLE "education_item"`); + await queryRunner.query(`DROP TABLE "user_follow"`); + await queryRunner.query(`DROP TABLE "page_follow"`); + await queryRunner.query(`DROP TABLE "page"`); + await queryRunner.query(`DROP TABLE "conversation"`); + await queryRunner.query(`DROP TABLE "tag"`); + await queryRunner.query(`DROP TABLE "offer"`); + await queryRunner.query(`DROP TABLE "offer_application"`); + await queryRunner.query(`DROP TABLE "comment_vote"`); + await queryRunner.query(`DROP TABLE "comment"`); + await queryRunner.query(`DROP TABLE "space_subscription"`); + await queryRunner.query(`DROP TABLE "space"`); + await queryRunner.query(`DROP TABLE "vote"`); + await queryRunner.query(`DROP TABLE "post"`); + await queryRunner.query(`DROP TABLE "user"`); + } + +} diff --git a/src/migrations/1660494342131-AddSpaces.ts b/src/migrations/1660494342131-AddSpaces.ts new file mode 100644 index 0000000..a614519 --- /dev/null +++ b/src/migrations/1660494342131-AddSpaces.ts @@ -0,0 +1,39 @@ +import { __prod__ } from '@constants' +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddSpaces1660494342131 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + BEGIN; + INSERT INTO space ("spaceName") VALUES ('CS'); + INSERT INTO space ("spaceName") VALUES ('economics'); + INSERT INTO space ("spaceName") VALUES ('politics'); + INSERT INTO space ("spaceName") VALUES ('arts'); + INSERT INTO space ("spaceName") VALUES ('law'); + INSERT INTO space ("spaceName") VALUES ('agriculture'); + INSERT INTO space ("spaceName") VALUES ('tourism'); + INSERT INTO space ("spaceName") VALUES ('engineering'); + INSERT INTO space ("spaceName") VALUES ('science'); + INSERT INTO space ("spaceName") VALUES ('history'); + INSERT INTO space ("spaceName") VALUES ('philosophy'); + INSERT INTO space ("spaceName") VALUES ('maths'); + INSERT INTO space ("spaceName") VALUES ('physics'); + INSERT INTO space ("spaceName") VALUES ('chemistry'); + INSERT INTO space ("spaceName") VALUES ('biology'); + INSERT INTO space ("spaceName") VALUES ('sports'); + COMMIT; + `) + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM space WHERE true; + `) + } + } + +} diff --git a/src/migrations/1660494342132-AddUsers.ts b/src/migrations/1660494342132-AddUsers.ts new file mode 100644 index 0000000..5ce04e9 --- /dev/null +++ b/src/migrations/1660494342132-AddUsers.ts @@ -0,0 +1,135 @@ +import { __prod__ } from '@constants' +import { faker } from '@faker-js/faker' +import _ from 'lodash' +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddUsers1660494342132 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(` + INSERT INTO "user" ("username", "email", "password") VALUES ('admin', 'quyanh.nguyen@helsinki.fi', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nullett0', 'drubbert0@oracle.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-02-21 05:19:19', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('evigers1', 'eolyet1@a8.net', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-08-06 04:15:03', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tpaoloni2', 'fgaenor2@unesco.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-02-23 16:59:03', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('cmacconneely3', 'gglanfield3@google.pl', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-09-30 18:06:03', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('bcraghead4', 'sickovitz4@sakura.ne.jp', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-08-25 23:12:55', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ieat5', 'hkopf5@yellowbook.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-06-14 10:33:57', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('harling6', 'hlortz6@wsj.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-02-18 15:17:28', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lbrandel7', 'kbuyers7@slate.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-02-21 06:04:22', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lkatte8', 'clanston8@i2i.jp', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-09-18 20:16:31', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('baronowitz9', 'spossek9@upenn.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-09-03 06:56:17', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mkalinovicha', 'mmcenerya@unicef.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-11-15 03:21:09', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dburkettb', 'cscuphamb@un.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-10-09 05:42:19', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('bmitchelhillc', 'ccoathc@unc.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-12-22 04:00:30', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('eadamczewskid', 'crozenzweigd@amazonaws.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-08-16 17:24:33', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fbeszante', 'mfloate@arstechnica.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-04-18 11:23:07', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('prozsaf', 'eturonef@live.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-09-12 22:12:07', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mvittelg', 'nreadittg@icq.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-02-24 00:11:29', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fcasoh', 'fhaversh@independent.co.uk', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-02-20 03:35:50', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('wmugfordi', 'jcuestai@cmu.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-11-17 03:05:33', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mbruckenthalj', 'skhrishtafovichj@mashable.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-05-19 17:03:47', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gmacgormank', 'khurrellk@tiny.cc', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-07-11 06:32:04', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('plesliel', 'nmclesel@oaic.gov.au', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-06-08 08:03:24', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rlangmeadm', 'hlamymanm@foxnews.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-08-19 21:44:19', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gchapelhown', 'wcoupmann@posterous.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-06-04 17:34:58', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nbarwacko', 'mtethcoteo@deviantart.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-05-25 20:09:36', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lcardenasp', 'jbridep@wiley.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-03-16 03:10:29', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fwhatsizeq', 'wadiscotq@berkeley.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-02-27 05:13:13', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lbeardowr', 'kdashkovichr@youtube.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-05-01 03:39:50', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('sdellos', 'wdartnells@smugmug.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-11-01 13:16:25', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('clindwassert', 'afustt@yahoo.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-05-26 16:18:36', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('sgoffu', 'krochesteru@narod.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-01-28 03:50:40', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('cimlackev', 'bnorthamv@storify.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-09-17 13:07:23', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hnaerupw', 'ccunniffw@liveinternet.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-07-24 15:57:24', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('brylandsx', 'sgutsellx@vkontakte.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-07-13 16:24:12', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mbottomleyy', 'glamberthy@paginegialle.it', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-01-10 09:11:07', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gmacgaughiez', 'bbarrittz@youtube.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-09-03 12:45:37', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mdochon10', 'jpostians10@hao123.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-01-24 15:08:32', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ksommerville11', 'lslader11@histats.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-03-23 01:38:11', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('sclemitt12', 'wbickerdike12@gnu.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-03-09 07:20:39', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rmertin13', 'mphilott13@netscape.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-07-17 11:42:00', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jcreane14', 'borrill14@simplemachines.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-07-15 16:42:57', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hbrood15', 'mgudgin15@go.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-09-14 00:34:51', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('shofler16', 'dchappel16@economist.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-12-03 17:18:12', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ndavydychev17', 'eeggleton17@goodreads.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-07-01 12:55:01', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('bcroutear18', 'droe18@samsung.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-09-01 23:30:53', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dsalvidge19', 'wmollitt19@chicagotribune.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-04-04 18:28:56', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rthundercliffe1a', 'baveling1a@yale.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-06-23 11:25:30', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('chealeas1b', 'jbuckley1b@theatlantic.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-04-13 01:16:19', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dmayers1c', 'aphetteplace1c@a8.net', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-03-08 09:43:10', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('trope1d', 'brosone1d@free.fr', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-03-12 04:33:36', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('pizzard1e', 'dmahoney1e@slate.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-08-29 22:48:53', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hmcilwrick1f', 'fbullock1f@dedecms.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-04-13 12:16:56', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mkilfoyle1g', 'mduffell1g@drupal.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-04-06 00:06:11', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('aoutright1h', 'lshouler1h@tinyurl.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-03-10 01:46:56', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('vphethean1i', 'egrabban1i@cdbaby.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-08-03 22:31:30', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('thammand1j', 'memms1j@myspace.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-03-17 20:05:07', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('scabotto1k', 'lmorey1k@washington.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-08-02 14:07:16', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ahardwin1l', 'avaller1l@fema.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-08-02 04:00:56', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hbatha1m', 'omonini1m@sbwire.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-12-10 00:48:51', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gfrigot1n', 'fjoderli1n@linkedin.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-11-05 10:10:38', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gcorn1o', 'cpainten1o@usgs.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-11-25 18:56:43', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mbecken1p', 'bflippen1p@addtoany.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-03-25 20:36:23', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nvangeffen1q', 'wverbrugge1q@amazon.co.jp', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-08-05 04:05:19', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('shelversen1r', 'idemanche1r@chron.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-09-11 21:39:30', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('vepperson1s', 'pwalburn1s@example.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-03-14 03:48:40', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('cluckin1t', 'nmehaffey1t@webmd.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-07-24 13:26:57', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('edecort1u', 'jcanto1u@artisteer.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-08-09 17:01:53', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jell1v', 'dhissett1v@angelfire.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-11-14 15:37:08', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('kseamarke1w', 'fdeniskevich1w@pcworld.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-02-23 14:51:15', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jteal1x', 'rmustard1x@clickbank.net', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-08-11 03:57:23', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('colenane1y', 'jisakov1y@intel.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-08-21 15:36:04', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('junwins1z', 'acuredell1z@hexun.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-06-26 13:46:42', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jpetyt20', 'ahuff20@examiner.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-01-29 17:28:07', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mgarrelts21', 'rcopeman21@vistaprint.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-09-16 22:44:37', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('aplacidi22', 'mfeldharker22@usda.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-07-20 20:47:24', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tloft23', 'mdur23@mlb.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-05-02 09:20:22', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nbladon24', 'mplose24@gizmodo.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-07-21 04:32:58', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gmccaughan25', 'glindl25@yandex.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-12-20 15:15:27', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ocasini26', 'chasloch26@360.cn', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-07-06 09:25:32', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hwoolmore27', 'ltriggel27@taobao.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-04-10 10:13:08', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mfaccini28', 'klasselle28@epa.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-11-12 04:27:10', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nnoddings29', 'nsiddons29@studiopress.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-04-26 00:16:37', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dscarlin2a', 'jkarys2a@marriott.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-01-24 08:07:07', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jfields2b', 'lrooze2b@wordpress.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-02-07 03:05:29', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fedgington2c', 'gsurmeir2c@tmall.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-01-27 07:05:49', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('kmeekings2d', 'ayeabsley2d@surveymonkey.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-01-11 22:02:29', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tkirkebye2e', 'mgipps2e@state.tx.us', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-02-06 16:28:54', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tosmar2f', 'kguntrip2f@shinystat.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-06-02 01:18:00', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mdubber2g', 'cheadey2g@usgs.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-07-03 06:12:22', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hfeldklein2h', 'peronie2h@cyberchimps.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-10-21 13:13:28', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ccaneo2i', 'iadaway2i@aboutads.info', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-07-14 16:22:46', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lyansons2j', 'tauckland2j@home.pl', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-04-02 17:08:09', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('savann2k', 'hsnelman2k@aboutads.info', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-10-09 15:14:30', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('palyokhin2l', 'piacovolo2l@t.co', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-01-03 09:12:00', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dmacallen2m', 'kgorgler2m@instagram.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-01-03 11:50:11', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rpollendine2n', 'emaccahey2n@usda.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-12-06 15:51:04', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ndurie2o', 'wgrigolashvill2o@ask.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-11-05 17:40:10', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mohdirscoll2p', 'gbrombell2p@illinois.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-05-28 17:29:47', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('abetke2q', 'lcurrie2q@paginegialle.it', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-06-29 00:06:53', '${faker.address.latitude()}', '${faker.address.longitude()}'); + INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('pberrow2r', 'echettoe2r@techcrunch.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-09-05 01:58:34', '${faker.address.latitude()}', '${faker.address.longitude()}'); + `) + + for (let i = 1; i <= 101; i++) { // for each user to be followed + let queryString = '' + const numberOfFollowers = i == 1 ? 100 : _.random(1, 100) + const arr = _.sampleSize(_.range(2, 102), numberOfFollowers).filter(n => n != i) + for (let y = 0; y < arr.length; y++) { + queryString += `\nINSERT INTO user_follow ("followedUserId", "followingUserId") VALUES (${i}, ${arr[y]});` + } + await queryRunner.query(queryString) + } + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM "user" WHERE true; + DELETE FROM "user_follow" WHERE true; + `) + } + } + +} \ No newline at end of file diff --git a/src/migrations/1660494342133-AddAdminAsModToSpaces.ts b/src/migrations/1660494342133-AddAdminAsModToSpaces.ts new file mode 100644 index 0000000..63e811d --- /dev/null +++ b/src/migrations/1660494342133-AddAdminAsModToSpaces.ts @@ -0,0 +1,34 @@ +import { __prod__ } from '@constants' +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddAdminAsModToSpaces1660494342133 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + CREATE OR REPLACE PROCEDURE main() + AS $$ + DECLARE + s integer; + BEGIN + FOR s IN + SELECT id FROM space + LOOP + INSERT INTO space_mods_user ("spaceId", "userId") VALUES (s, (SELECT id FROM "user" WHERE username = 'admin')); + END LOOP; + END; + $$ LANGUAGE plpgsql; + CALL main(); + `) + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM space_mods_user WHERE "userId" = (SELECT id FROM "user" WHERE username = 'admin'); + `) + } + } + +} \ No newline at end of file diff --git a/src/migrations/1660494342134-AddPages.ts b/src/migrations/1660494342134-AddPages.ts new file mode 100644 index 0000000..43d65c7 --- /dev/null +++ b/src/migrations/1660494342134-AddPages.ts @@ -0,0 +1,337 @@ +import { __prod__ } from '@constants' +import _ from 'lodash' +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AddPosts1660494342134 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + INSERT INTO page ("pageName", "fullPageName") VALUES ('osiggin0', 'Turcotte-Cronin'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tbasley1', 'Wolf-Price'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mbarthel2', 'Hagenes, Russel and Block'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('kduckinfield3', 'Koelpin, Rowe and O''Kon'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mmougeot4', 'Stroman-Lind'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('medden5', 'Wintheiser LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('astoney6', 'McGlynn, Hoppe and Little'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('cventam7', 'Corwin-Gerhold'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('cpragnall8', 'Fahey, Hayes and Predovic'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('gkissick9', 'Abbott Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('dkedwella', 'Bode LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('osprostonb', 'Hartmann, Dibbert and Lang'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('snorkettc', 'Reinger LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tjurczikd', 'Hauck-Jones'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('fhoofee', 'Kiehn-Spinka'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('hgasperif', 'Reilly and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('lbennitg', 'Ziemann LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tclaireh', 'Hegmann-Wiza'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('vbutteli', 'Stracke LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('kmarchantj', 'Hermann and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('cdonoghuek', 'Runolfsdottir, Ziemann and Grady'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ojelksl', 'Schroeder, Smith and Wunsch'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('jmallenderm', 'Macejkovic, Labadie and O''Kon'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tsiemonsn', 'Goldner LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('amethingamo', 'Champlin, Ruecker and Collier'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('dleevesp', 'Feil LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('jdoumerq', 'Larkin and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mkestenr', 'Donnelly and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('lpostins', 'Wisoky Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('avolkt', 'Thiel, Jacobson and Hermiston'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tbabinskiu', 'Johnston and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('csavilev', 'Rosenbaum LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('vmayesw', 'Jenkins Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ywillcockx', 'Satterfield and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('dbulfieldy', 'Steuber-Kreiger'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tcottrillz', 'Fahey, Rowe and Champlin'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mlangthorne10', 'Rogahn and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('zparratt11', 'Schaden and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('edoucette12', 'Gulgowski, Paucek and Herman'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('gwillavize13', 'Crona, Effertz and King'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('rziemke14', 'Larkin-Walter'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tpickover15', 'Roob and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('awaghorn16', 'Cormier, Graham and Metz'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('lspruce17', 'Ebert-Bosco'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('sarmistead18', 'Cremin Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('odanick19', 'Harber Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('dflipsen1a', 'Kirlin and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('pbygrave1b', 'Kessler, Kutch and Streich'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('dbattabee1c', 'O''Conner, Stehr and Becker'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('abalfre1d', 'Beatty Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('elewer1e', 'Bogan, O''Connell and Klocko'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('hmclaine1f', 'Zemlak-Stiedemann'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mbevan1g', 'Borer, McKenzie and Mueller'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('leldon1h', 'Heller, Johnson and Kuvalis'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('gthunnercliff1i', 'Adams Inc'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('scrowhurst1j', 'Boyle, Gaylord and Flatley'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mgilloran1k', 'Mosciski and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('cbertholin1l', 'Hodkiewicz-Wolf'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ecastiglione1m', 'Kshlerin, Hills and Jacobs'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('sgearty1n', 'Mertz Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('bpatesel1o', 'Yundt and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('vhoudmont1p', 'Stanton-Jacobs'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('cpikhno1q', 'Shanahan, Lueilwitz and Wolf'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('lpaute1r', 'Kiehn, Emard and Lemke'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('rabramchik1s', 'Smith LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('csommerville1t', 'Crooks-Monahan'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('jjaume1u', 'Leuschke LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('alethbury1v', 'Howe, Hand and Jerde'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('gdoogood1w', 'Howe-Streich'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ldrabble1x', 'Balistreri-Greenfelder'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('pudie1y', 'Wisozk Inc'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('econiam1z', 'Legros, Weimann and Stiedemann'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('pkipping20', 'Braun, Hessel and Blick'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mcaccavella21', 'Skiles-Douglas'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('jbelhomme22', 'Schoen, Rice and Cassin'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('dsikora23', 'Swift, Glover and Jerde'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('gnorewood24', 'O''Kon and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('jscowcraft25', 'Cronin, Leffler and Spencer'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('wjeffreys26', 'Ritchie Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('amaccoughen27', 'Hickle, Price and Bergstrom'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('oseywood28', 'Bailey Group'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('kdennison29', 'Hettinger LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('etzuker2a', 'McKenzie Inc'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mguidoni2b', 'Bailey-Hartmann'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('etaaffe2c', 'Halvorson, Pagac and Jones'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('bglynne2d', 'Kessler Inc'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('jmatejovsky2e', 'Blick, Greenholt and Nader'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ftilio2f', 'Nitzsche, Auer and Kemmer'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('skhadir2g', 'Beier-Osinski'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('blanktree2h', 'Luettgen LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('htofts2i', 'Pagac, Kuhlman and Ernser'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ccrisford2j', 'Pollich LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('vwinterflood2k', 'Reichel LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('mdominey2l', 'Hahn-Green'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ngrave2m', 'Farrell LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('ttoomey2n', 'Yost-Reichel'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tdog2o', 'Johnston LLC'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('aseabon2p', 'Goyette and Sons'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('tlutsch2q', 'Cassin-Gottlieb'); + INSERT INTO page ("pageName", "fullPageName") VALUES ('lwickardt2r', 'Marvin, Hand and McCullough'); + + INSERT INTO page_owners_user ("pageId", "userId") VALUES (1, 61); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (2, 84); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (3, 56); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (4, 73); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (5, 17); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (6, 43); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (7, 56); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (8, 81); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (9, 23); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (10, 96); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (11, 71); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (12, 16); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (13, 86); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (14, 74); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (15, 44); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (16, 56); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (17, 49); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (18, 71); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (19, 29); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (20, 48); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (21, 53); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (22, 11); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (23, 60); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (24, 22); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (25, 80); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (26, 54); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (27, 95); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (28, 41); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (29, 19); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (30, 31); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (31, 39); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (32, 14); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (33, 40); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (34, 8); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (35, 36); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (36, 21); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (37, 46); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (38, 29); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (39, 79); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (40, 91); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (41, 41); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (42, 86); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (43, 46); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (44, 94); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (45, 5); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (46, 51); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (47, 63); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (48, 26); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (49, 24); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (50, 6); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (51, 27); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (52, 32); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (53, 94); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (54, 4); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (55, 3); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (56, 77); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (57, 62); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (58, 95); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (59, 49); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (60, 65); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (61, 14); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (62, 70); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (63, 73); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (64, 23); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (65, 26); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (66, 8); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (67, 35); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (68, 30); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (69, 41); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (70, 20); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (71, 44); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (72, 76); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (73, 43); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (74, 90); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (75, 35); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (76, 77); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (77, 52); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (78, 84); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (79, 7); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (80, 28); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (81, 22); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (82, 90); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (83, 92); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (84, 58); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (85, 18); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (86, 6); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (87, 36); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (88, 19); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (89, 62); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (90, 3); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (91, 19); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (92, 16); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (93, 95); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (94, 73); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (95, 34); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (96, 39); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (97, 27); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (98, 93); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (99, 49); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (100, 88); + + INSERT INTO page_owners_user ("pageId", "userId") VALUES (1, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (2, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (3, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (4, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (5, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (6, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (7, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (8, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (9, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (10, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (11, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (12, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (13, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (14, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (15, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (16, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (17, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (18, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (19, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (20, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (21, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (22, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (23, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (24, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (25, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (26, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (27, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (28, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (29, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (30, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (31, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (32, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (33, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (34, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (35, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (36, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (37, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (38, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (39, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (40, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (41, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (42, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (43, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (44, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (45, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (46, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (47, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (48, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (49, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (50, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (51, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (52, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (53, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (54, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (55, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (56, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (57, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (58, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (59, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (60, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (61, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (62, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (63, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (64, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (65, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (66, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (67, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (68, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (69, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (70, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (71, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (72, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (73, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (74, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (75, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (76, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (77, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (78, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (79, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (80, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (81, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (82, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (83, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (84, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (85, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (86, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (87, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (88, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (89, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (90, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (91, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (92, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (93, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (94, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (95, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (96, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (97, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (98, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (99, 1); + INSERT INTO page_owners_user ("pageId", "userId") VALUES (100, 1); + `) + + for (let i = 1; i <= 100; i++) { // for each page + let queryString = '' + const numberOfFollowers = _.random(1, 100) + // Generate an array of unique random integers (min 2, max 101) with length equal to numberOfFollowers + const arr = _.sampleSize(_.range(2, 102), numberOfFollowers) + for (let y = 0; y < arr.length; y++) { // a page can have a maximum of 100 followers + queryString += `\nINSERT INTO page_follow ("pageId", "userId") VALUES (${i}, ${arr[y]});` + } + await queryRunner.query(queryString) + } + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM page WHERE true; + DELETE FROM page_owners_user WHERE true; + DELETE FROM page_follow WHERE true; + `) + } + } + +} diff --git a/src/migrations/1660494342135-AddSpaceSubscribers.ts b/src/migrations/1660494342135-AddSpaceSubscribers.ts new file mode 100644 index 0000000..4228836 --- /dev/null +++ b/src/migrations/1660494342135-AddSpaceSubscribers.ts @@ -0,0 +1,29 @@ +import { __prod__ } from '@constants' +import _ from 'lodash' +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddSpaceSubscribers1660494342135 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + for (let i = 1; i <= 16; i++) { // for each space + let queryString = '' + const numberOfSubscribers = _.random(1, 100) + const arr = _.sampleSize(_.range(2, 102), numberOfSubscribers) + for (let y = 0; y < arr.length; y++) { + queryString += `\nINSERT INTO space_subscription ("spaceId", "userId") VALUES (${i}, ${arr[y]});` + } + await queryRunner.query(queryString) + } + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM space_subscription WHERE true; + `) + } + } + +} \ No newline at end of file diff --git a/src/migrations/1660494342136-AddTags.ts b/src/migrations/1660494342136-AddTags.ts new file mode 100644 index 0000000..8e7ec41 --- /dev/null +++ b/src/migrations/1660494342136-AddTags.ts @@ -0,0 +1,33 @@ +import { __prod__ } from '@constants' +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AddTags1660494342136 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`--sql + BEGIN; + INSERT INTO tag (name) VALUES ('Question'); + INSERT INTO tag (name) VALUES ('Help'); + INSERT INTO tag (name) VALUES ('Fundraising'); + INSERT INTO tag (name) VALUES ('Introduction'); + INSERT INTO tag (name) VALUES ('Self'); + INSERT INTO tag (name) VALUES ('AMA'); + INSERT INTO tag (name) VALUES ('Resources'); + INSERT INTO tag (name) VALUES ('Educational'); + INSERT INTO tag (name) VALUES ('Poll'); + INSERT INTO tag (name) VALUES ('Recruitment'); + INSERT INTO tag (name) VALUES ('Contract'); + INSERT INTO tag (name) VALUES ('Networking'); + INSERT INTO tag (name) VALUES ('Opinion'); + INSERT INTO tag (name) VALUES ('Other'); + COMMIT; + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`--sql + DELETE FROM tag WHERE true; + `) + } + +} diff --git a/src/migrations/1660494342137-AddPosts.ts b/src/migrations/1660494342137-AddPosts.ts new file mode 100644 index 0000000..178cac9 --- /dev/null +++ b/src/migrations/1660494342137-AddPosts.ts @@ -0,0 +1,43 @@ +import { __prod__ } from '@constants' +import { faker } from '@faker-js/faker' +import _ from 'lodash' +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddPosts1660494342137 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + let queryString = '' + for (let i = 0; i < 1000; i++) { + const title = faker.company.catchPhrase() + const text = faker.lorem.paragraph() + const spaceId = _.random(1, 16) + const creatorId = faker.datatype.boolean() ? _.random(2, 101) : null + const pageCreatorId = creatorId ? null : _.random(1, 100) + const creatorType = creatorId ? 'user' : 'page' + const createdAt = faker.date.between('2020-01-01T00:00:00.000Z', '2021-01-01T00:00:00.000Z').toISOString() + queryString += `\nINSERT INTO post ("title", "text", "spaceId", "creatorId", "pageCreatorId", "createdAt", "creatorType") VALUES ('${title}', '${text}', ${spaceId}, ${creatorId}, ${pageCreatorId}, '${createdAt}', '${creatorType}');` + } + await queryRunner.query(queryString) + queryString = '' + for (let i = 1; i < 1001; i++) { + const numberOfTags = _.random(1, 4) + const arr = _.sampleSize(_.range(1, 15), numberOfTags) + for (let y = 0; y < numberOfTags; y++) { + queryString += `\nINSERT INTO post_tags_tag ("postId", "tagId") VALUES (${i}, ${arr[y]});` + } + } + await queryRunner.query(queryString) + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM post WHERE true; + DELETE FROM post_tags_tag WHERE true; + `) + } + } + +} \ No newline at end of file diff --git a/src/migrations/1660494342138-AddOffers.ts b/src/migrations/1660494342138-AddOffers.ts new file mode 100644 index 0000000..6a210a8 --- /dev/null +++ b/src/migrations/1660494342138-AddOffers.ts @@ -0,0 +1,67 @@ +import { __prod__ } from '@constants' +import { faker } from '@faker-js/faker' +import _ from 'lodash' +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class AddOffers1660494342138 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (!__prod__) { + let queryString = '' + for (let i = 0; i < 1000; i++) { + const title = faker.name.jobTitle().replaceAll('\'', '') + const workplace = faker.company.name().replaceAll('\'', '') + const address = faker.address.streetAddress(true).replaceAll('\'', '') + const recruiting = faker.datatype.boolean() + const employmentType = _.sample([ + 'Full-time', + 'Part-time', + 'Freelance', + 'Online', + 'Internship', + 'Temporary', + 'Contract', + 'Volunteer', + 'Other' + ]) + const currency = faker.finance.currencySymbol() + const salaryRange = `${currency}${_.random(0, 10000)} - ${currency}${_.random(10000, 999999)}` + const department = faker.name.jobArea() + const requirements = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.` + const benefits = `Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?` + const description = `At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.` + const spaceId = _.random(1, 16) + const creatorId = faker.datatype.boolean() ? _.random(2, 101) : null + const pageCreatorId = creatorId ? null : _.random(1, 100) + const creatorType = creatorId ? 'user' : 'page' + const createdAt = faker.date.between('2020-01-01T00:00:00.000Z', '2021-01-01T00:00:00.000Z').toISOString() + queryString += + `\nINSERT INTO offer ("title", "workplace", "address", "recruiting", "employmentType", "salaryRange", "department", "requirements", "benefits", "description", "spaceId", "creatorId", "pageCreatorId", "creatorType", "createdAt") VALUES ('${title}', '${workplace}', '${address}', ${recruiting}, '${employmentType}', '${salaryRange}', '${department}', '${requirements}', '${benefits}', '${description}', ${spaceId}, ${creatorId}, ${pageCreatorId}, '${creatorType}', '${createdAt}') ;` + } + await queryRunner.query(queryString) + + queryString = '' + for (let i = 1; i < 1001; i++) { + const offerId = i + const numberOfApplications = _.random(0, 3) + const arr = _.sampleSize(_.range(2, 102), numberOfApplications) + for (let y = 0; y < numberOfApplications; y++) { + const status = _.sample(['applied', 'accepted', 'rejected']) + queryString += + `\nINSERT INTO offer_application ("offerId", "userId", "status") VALUES (${offerId}, ${arr[y]}, '${status}') ;` + } + } + await queryRunner.query(queryString) + } + } + + public async down(queryRunner: QueryRunner): Promise { + if (!__prod__) { + await queryRunner.query(`--sql + DELETE FROM offer WHERE true; + DELETE FROM offer_application WHERE true; + `) + } + } + +} \ No newline at end of file diff --git a/src/resolvers/comment.ts b/src/resolvers/comment.ts new file mode 100644 index 0000000..81473eb --- /dev/null +++ b/src/resolvers/comment.ts @@ -0,0 +1,202 @@ +import { Comment, CommentVote, Page, Post, User } from '@entities' +import { PaginatedComments } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { toPostgresTime } from '@utils' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' + +@Resolver(Comment) +export class CommentResolver { + + @FieldResolver(() => User) + creator( + @Root() root: Comment, + @Ctx() { userLoader }: Context + ) { + return root.creatorId ? userLoader.load(root.creatorId) : null + } + + @FieldResolver(() => Page) + pageCreator( + @Root() root: Comment, + @Ctx() { pageLoader }: Context + ) { + return root.pageCreatorId ? pageLoader.load(root.pageCreatorId) : null + } + + @Query(() => PaginatedComments) + async comments( + @Ctx() { req, orm }: Context, + @Arg('postId', () => Int) postId: number, + @Arg('limit', () => Int) limit: number, + @Arg('cursor', () => String, { nullable: true }) cursor: string | null, + ): Promise { + const { userId } = req.session + + const realLimit = Math.min(limit, 5) + const realLimitPlusOne = realLimit + 1 + + const comments = await orm.query(` + SELECT * + FROM comment c + WHERE c."postId" = ${postId} + ${cursor ? `AND c."createdAt" < '${toPostgresTime(cursor)}'` : ''} + ORDER BY + ${userId ? `c."creatorId" = ${userId} DESC,` : ''} + c.points DESC, + c."createdAt" DESC + LIMIT ${realLimitPlusOne} + `) + + return { + comments: comments.slice(0, realLimit), + hasMore: comments.length - 1 == realLimit + } + } + + @FieldResolver(() => Int, { nullable: true }) + async voteStatus( + @Root() root: Comment, + @Ctx() { req, commentVoteLoader }: Context + ) { + return req.session?.userId ? + (await commentVoteLoader.load({ commentId: root.id, userId: req.session.userId }))?.value + : + null + } + + @UseMiddleware(isAuth) + @Mutation(() => Comment) + async createComment( + @Arg('postId', () => Int) postId: number, + @Arg('text', () => String) text: string, + @Arg('pageId', () => Int, { nullable: true }) pageId: number, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const post = await Post.findOneBy({ id: postId }) + if (!post) { + return null + } + if (pageId) { + const page = await Page.findOne({ where: { id: pageId } }) + if (!page) { + throw new Error('Page does not exist!') + } + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${userId} AND "pageId" = ${pageId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + } + return await Comment.create({ + text, + postId, + creatorType: pageId ? 'page' : 'user', + creatorId: pageId ? undefined : userId, + pageCreatorId: pageId, + }).save() + } + + @Mutation(() => Comment, { nullable: true }) + @UseMiddleware(isAuth) + async updateComment( + @Arg('id', () => Int) id: number, + @Arg('text', () => String) text: string, + @Ctx() { orm, req }: Context + ): Promise { + const comment = await Comment.findOne({ where: { id } }) + if (comment) { + if (comment.creatorType == 'user') { + if (comment.creatorId != req.session.userId) { + throw new Error('Not authorised!') + } + } + else { + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${comment.pageCreatorId}; + `) + if (ownerProof.length == 0) { + throw new Error('Not authorised!') + } + } + comment.text = text + return comment.save() + } + else { + throw new Error('Post not found.') + } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteComment( + @Arg('id', () => Int) id: number, + @Ctx() { orm, req }: Context + ): Promise { + const comment = await Comment.findOneBy({ id }) + if (!comment) { + throw new Error('Comment not found!') + } + if (comment.creatorType == 'user') { + if (comment.creatorId != req.session.userId) { + throw new Error('Not authorised!') + } + } + else { + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${comment.pageCreatorId}; + `) + if (ownerProof.length == 0) { + throw new Error('Not authorised!') + } + } + await comment.remove() + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async voteComment( + @Arg('commentId', () => Int) commentId: number, + @Arg('value', () => Int) value: number, + @Ctx() { req, orm }: Context + ) { + const { userId } = req.session + if (value == 0) return false + const realValue = value > 0 ? 1 : value < 0 ? -1 : 0 + + const commentVote = await CommentVote.findOne({ where: { commentId, userId } }) + + if (commentVote) { + if (commentVote.value == realValue) { + await commentVote.remove() + const comment = await Comment.findOne({ where: { id: commentId } }) + if (comment) { + comment.points = comment.points + (realValue == 1 ? -1 : 1) + await comment.save() + } + } + else { + await orm.transaction(async transactionalEntityManager => { + commentVote.value = realValue + await transactionalEntityManager.save(commentVote) + await transactionalEntityManager.increment(Comment, { id: commentId }, 'points', realValue == -1 ? -2 : 2) + }) + } + } + else { + await orm.transaction(async transactionalEntityManager => { + const newCommentVote = new CommentVote() + newCommentVote.userId = userId! + newCommentVote.commentId = commentId + newCommentVote.value = realValue + await transactionalEntityManager.save(newCommentVote) + await transactionalEntityManager.increment(Comment, { id: commentId }, 'points', realValue) + }) + } + + return true + } +} \ No newline at end of file diff --git a/src/resolvers/conversation.ts b/src/resolvers/conversation.ts new file mode 100644 index 0000000..98032d3 --- /dev/null +++ b/src/resolvers/conversation.ts @@ -0,0 +1,215 @@ +import { ConversationCol, ConversationInFirestore, Inbox, InboxCol, Message } from '@collections' +import { s3 } from '@configs' +import { Conversation as ConversationEnt, User } from '@entities' +import { CreateConversationResponse, InboxResponse, MessageInput } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { getExtensionFromFilename } from '@utils' +import { FileUpload } from 'graphql-upload' +import { Arg, Ctx, Int, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql' +import { ArrayContains, In } from 'typeorm' +import { v4 } from 'uuid' + +@Resolver(ConversationInFirestore) +export class ConversationResolver { + + @UseMiddleware(isAuth) + @Query(() => [ConversationEnt]) + async conversations( + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + return await ConversationEnt.find({ + where: { + participants: ArrayContains([userId]) + } + }) + } + + @UseMiddleware(isAuth) + @Query(() => [User]) + async participants( + @Arg('firestoreCollectionId', () => String) firestoreCollectionId: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const convo = await ConversationEnt.findOneBy({ firestoreCollectionId }) + if (!convo) { + return [] + } + if (!convo.participants.includes(userId!)) { + throw new Error('Not authorised!') + } + return User.findBy({ id: In(convo.participants) }) + } + + @UseMiddleware(isAuth) + @Query(() => [InboxResponse]) + async inboxes( + @Arg('firestoreCollectionIds', () => [String]) firestoreCollectionIds: string[], + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const convos = await ConversationEnt.findBy({ firestoreCollectionId: In(firestoreCollectionIds) }) + return Promise.all(firestoreCollectionIds.map(async fci => { + const convo = convos.find(c => c.firestoreCollectionId == fci)! + const partnerId = convo.participants.find(id => id != userId) + const partner = (await User.findOne({ where: { id: partnerId } }))! + return { + partner, + firestoreCollectionId: fci + } + })) + } + + + @UseMiddleware(isAuth) + @Mutation(() => CreateConversationResponse) + async createConversation( + @Arg('partnerUsername', () => String) partnerUsername: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const partner = await User.findOne({ where: { username: partnerUsername } }) + if (!partner) { + return { + errors: [{ + field: 'partnerUsername', + message: 'Invalid username.' + }] + } + } + const partnerId = partner.id + const existingConvo = await ConversationEnt.findOne({ + where: { + participants: ArrayContains([userId, partnerId]) + } + }) + if (existingConvo) { + return { + errors: [{ + field: 'partnerUsername', + message: 'Conversation already exists.' + }] + } + } + const convo = new ConversationInFirestore() + const convoItem = await ConversationCol.create(convo) + let inbox = await InboxCol.whereEqualTo('userId', userId!).findOne() + let partnerInbox = await InboxCol.whereEqualTo('userId', partnerId!).findOne() + if (!inbox) { + inbox = new Inbox() + inbox.userId = userId! + inbox.conversationIds = [] + await InboxCol.create(inbox) + } + if (!partnerInbox) { + partnerInbox = new Inbox() + partnerInbox.userId = partnerId! + partnerInbox.conversationIds = [] + await InboxCol.create(partnerInbox) + } + inbox.conversationIds.push(convoItem.id) + partnerInbox.conversationIds.push(convoItem.id) + await InboxCol.update(inbox) + await InboxCol.update(partnerInbox) + const convoEnt = new ConversationEnt() + convoEnt.participants = [userId!, partnerId] + convoEnt.firestoreCollectionId = convoItem.id + await convoEnt.save() + return { conversation: convoItem } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteConversation( + @Arg('firestoreCollectionId', () => String) firestoreCollectionId: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const convo = await ConversationEnt.findOneBy({ firestoreCollectionId }) + if (!convo) { + throw new Error('Conversation not found.') + } + if (!convo.participants.includes(userId!)) { + throw new Error('Not authorised!') + } + const partnerId = convo.participants.find(id => id != userId) + const inbox = await InboxCol.whereEqualTo('userId', userId!).findOne() + const partnerInbox = await InboxCol.whereEqualTo('userId', partnerId!).findOne() + const firestoreConvo = await ConversationCol.findById(firestoreCollectionId) + await ConversationCol.delete(firestoreConvo.id) + inbox!.conversationIds = inbox!.conversationIds.filter(id => id != firestoreConvo.id) + partnerInbox!.conversationIds = partnerInbox!.conversationIds.filter(id => id != firestoreConvo.id) + await InboxCol.update(inbox!) + await InboxCol.update(partnerInbox!) + await convo.remove() + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => Message) + async createMessage( + @Arg('firestoreCollectionId', () => String) firestoreCollectionId: string, + @Arg('input', () => MessageInput) input: MessageInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { type, upload, text } = input + + const convoEnt = await ConversationEnt.findOne({ + where: { + firestoreCollectionId, + participants: ArrayContains([userId]) + } + }) + if (!convoEnt) { + throw new Error('Not authorised!') + } + const message = new Message() + message.senderId = userId! + message.type = type + + if (type == 'text') { + message.content = text! + } + else { + const { createReadStream, filename } = await upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `conversations/${convoEnt.id}/media/${type}s/${v4()}.${ext}`, + }).promise() + message.content = uploaded.Key + } + const convo = await ConversationCol.findById(firestoreCollectionId) + message.createdAt = new Date() + message.updatedAt = new Date() + return convo.messages.create(message) + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteMessage( + @Arg('firestoreCollectionId', () => String) firestoreCollectionId: string, + @Arg('messageDocumentId', () => String) messageDocumentId: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const convo = await ConversationCol.findById(firestoreCollectionId) + if (!convo) { + throw new Error('Conversation not found.') + } + const message = await convo.messages.findById(messageDocumentId) + if (!message) { + throw new Error('Message not found.') + } + if (message.senderId != userId) { + throw new Error('Not authorised!') + } + await convo.messages.delete(messageDocumentId) + return true + } +} \ No newline at end of file diff --git a/src/resolvers/cv.ts b/src/resolvers/cv.ts new file mode 100644 index 0000000..a2ab513 --- /dev/null +++ b/src/resolvers/cv.ts @@ -0,0 +1,67 @@ +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { CV } from '@entities' +import { UploadedFileResponse } from '@graphql-types' +import { isAuth } from '@middlewares' +import { getExtensionFromFilename } from '@utils' +import { GraphQLUpload, FileUpload } from 'graphql-upload' +import { v4 } from 'uuid' +import { Context } from '@types' +import { s3 } from '@configs' + +@Resolver(CV) +export class CVResolver { + + @FieldResolver() + async url( + @Root() cv: CV, + @Ctx() { req }: Context + ): Promise { + return cv.key ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: cv.key }) : '' + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadCV( + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/cvs/${filename}`, + }).promise() + await CV.create({ filename, key: uploaded.Key, userId }).save() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Query(() => [CV], { nullable: true }) + async cvs( + @Arg('userId', () => Int) userId: number, + ): Promise { + return CV.findBy({ userId }) + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteCV( + @Arg('id', () => Int) id: number, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const cv = await CV.findOneBy({ id, userId }) + if (!cv) { + throw new Error('Not authorised!') + } + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: cv.key! }).promise() + await cv.remove() + return true + } +} \ No newline at end of file diff --git a/src/resolvers/educationItem.ts b/src/resolvers/educationItem.ts new file mode 100644 index 0000000..1f6fdc4 --- /dev/null +++ b/src/resolvers/educationItem.ts @@ -0,0 +1,118 @@ +import { s3 } from '@configs' +import { EducationItem } from '@entities' +import { EducationItemInput, EducationItemResponse } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { getExtensionFromFilename, saveAttributes } from '@utils' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { v4 } from 'uuid' + +@Resolver(EducationItem) +export class EducationItemResolver { + @FieldResolver(() => String) + async photoUrl( + @Root() item: EducationItem + ): Promise { + return item.photo ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: item.photo }) : null + } + + @Query(() => [EducationItem]) + async educationItems( + @Arg('userId', () => Int) userId: number + ): Promise { + return (await EducationItem.find({ where: { userId } })) + } + + @UseMiddleware(isAuth) + @Mutation(() => EducationItemResponse) + async createEducationItem( + @Arg('input', () => EducationItemInput) input: EducationItemInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { school, status, startDate, endDate, photo } = input + if (!school) { + return { + errors: [{ + field: 'school', + message: 'School cannot be empty!' + }] + } + } + let key + if (photo) { + const { createReadStream, filename } = await photo + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/educationItems/${v4()}.${ext}` + }).promise() + key = uploaded.Key + } + return { + educationItem: await EducationItem.create({ + school, + status, + startDate, + endDate, + photo: key, + userId + }).save() + } + } + + @UseMiddleware(isAuth) + @Mutation(() => EducationItemResponse) + async updateEducationItem( + @Arg('id', () => Int) id: number, + @Arg('input', () => EducationItemInput) input: EducationItemInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { school, photo } = input + if (school != undefined && school == '') { + return { + errors: [{ + field: 'school', + message: 'School cannot be empty!' + }] + } + } + const educationItem = await EducationItem.findOne({ where: { id, userId } }) + if (!educationItem) { + throw new Error('Not authorised!') + } + saveAttributes(educationItem, input) + if (photo) { + const { createReadStream, filename, } = await photo + const ext = getExtensionFromFilename(filename) + const stream = createReadStream() + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/educationItems/${v4()}.${ext}`, + }).promise() + educationItem.photo = uploaded.Key + } + return { + educationItem: await educationItem.save() + } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteEducationItem( + @Arg('id', () => Int) id: number, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const educationItem = await EducationItem.findOne({ where: { id, userId } }) + if (!educationItem) { + throw new Error('Not authorised!') + } + await educationItem.remove() + return true + } +} \ No newline at end of file diff --git a/src/resolvers/experience.ts b/src/resolvers/experience.ts new file mode 100644 index 0000000..c0ad462 --- /dev/null +++ b/src/resolvers/experience.ts @@ -0,0 +1,118 @@ +import { s3 } from '@configs' +import { Experience } from '@entities' +import { ExperienceInput, ExperienceResponse } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { getExtensionFromFilename, saveAttributes } from '@utils' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { v4 } from 'uuid' + +@Resolver(Experience) +export class ExperienceResolver { + @FieldResolver(() => String) + async photoUrl( + @Root() item: Experience + ): Promise { + return item.photo ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: item.photo }) : null + } + + @Query(() => [Experience]) + async experiences( + @Arg('userId', () => Int) userId: number + ): Promise { + return (await Experience.find({ where: { userId } })) + } + + @UseMiddleware(isAuth) + @Mutation(() => ExperienceResponse) + async createExperience( + @Arg('input', () => ExperienceInput) input: ExperienceInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { title, workplace, startDate, endDate, photo } = input + if (!title) { + return { + errors: [{ + field: 'title', + message: 'Title cannot be empty!' + }] + } + } + let key + if (photo) { + const { createReadStream, filename } = await photo + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/experiences/${v4()}.${ext}`, + }).promise() + key = uploaded.Key + } + return { + experience: await Experience.create({ + title, + workplace, + startDate, + endDate, + photo: key, + userId + }).save() + } + } + + @UseMiddleware(isAuth) + @Mutation(() => ExperienceResponse) + async updateExperience( + @Arg('id', () => Int) id: number, + @Arg('input', () => ExperienceInput) input: ExperienceInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { title, photo } = input + if (title != undefined && title == '') { + return { + errors: [{ + field: 'title', + message: 'Title cannot be empty!' + }] + } + } + const experience = await Experience.findOne({ where: { id, userId } }) + if (!experience) { + throw new Error('Not authorised!') + } + saveAttributes(experience, input) + if (photo) { + const { createReadStream, filename, } = await photo + const ext = getExtensionFromFilename(filename) + const stream = createReadStream() + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/experiences/${v4()}.${ext}`, + }).promise() + experience.photo = uploaded.Key + } + return { + experience: await experience.save() + } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteExperience( + @Arg('id', () => Int) id: number, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const experience = await Experience.findOne({ where: { id, userId } }) + if (!experience) { + throw new Error('Not authorised!') + } + await experience.remove() + return true + } +} \ No newline at end of file diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts new file mode 100644 index 0000000..8053d6e --- /dev/null +++ b/src/resolvers/index.ts @@ -0,0 +1,14 @@ +export * from './user' +export * from './post' +export * from './space' +export * from './comment' +export * from './offer' +export * from './tag' +export * from './s3' +export * from './conversation' +export * from './page' +export * from './educationItem' +export * from './experience' +export * from './qualification' +export * from './cv' +export * from './quicksight' \ No newline at end of file diff --git a/src/resolvers/offer.ts b/src/resolvers/offer.ts new file mode 100644 index 0000000..bb2fd2f --- /dev/null +++ b/src/resolvers/offer.ts @@ -0,0 +1,431 @@ +import { s3 } from '@configs' +import { Offer, OfferApplication, Page, PageFollow, Space, SpaceSubscription, User, UserFollow } from '@entities' +import { OfferResponse, PaginatedOffers, OfferInputUpdate, OfferInputCreate } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { getExtensionFromFilename, saveAttributes, toPostgresTime } from '@utils' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { In } from 'typeorm' +import { v4 } from 'uuid' + +@Resolver(Offer) +export class OfferResolver { + + @FieldResolver(() => String) + async photoUrl( + @Root() offer: Offer + ): Promise { + return offer.photo ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: offer.photo }) : null + } + + @FieldResolver(() => [User], { nullable: true }) + async applications( + @Root() offer: Offer, + @Ctx() { orm, req, applicationLoader }: Context + ) { + const { userId } = req.session + + if (offer.creatorType == 'user') { + if (offer.creatorId != req.session.userId) { + return null + } + } + else { + if (!userId) { + return null + } + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${userId} AND "pageId" = ${offer.pageCreatorId}; + `) + if (ownerProof.length == 0) { + return null + } + } + return applicationLoader.load(offer.id) + } + + @FieldResolver(() => String) + applicationStatus( + @Root() root: Offer, + @Ctx() { req, applicationStatusLoader }: Context + ) { + const { userId } = req.session + return userId ? applicationStatusLoader.load({ offerId: root.id, userId }) : null + } + + @FieldResolver(() => User) + creator( + @Root() root: Offer, + @Ctx() { userLoader }: Context + ) { + return root.creatorId ? userLoader.load(root.creatorId) : null + } + + @FieldResolver(() => Page) + pageCreator( + @Root() root: Offer, + @Ctx() { pageLoader }: Context + ) { + return root.pageCreatorId ? pageLoader.load(root.pageCreatorId) : null + } + + + @FieldResolver(() => Space) + async space( + @Root() root: Offer, + @Ctx() { spaceLoader }: Context + ): Promise { + return spaceLoader.load(root.spaceId) + } + + @FieldResolver(() => Int) + async applicantsNo( + @Root() root: Offer, + @Ctx() { applicantsNoLoader }: Context + ): Promise { + return applicantsNoLoader.load(root.id) + } + + @Query(() => PaginatedOffers) + async offers( + @Ctx() { req, orm }: Context, + @Arg('spaceId', () => Int, { nullable: true }) spaceId: number, + @Arg('pageId', () => Int, { nullable: true }) pageId: number, + @Arg('userId', () => Int, { nullable: true }) userId: number, + @Arg('limit', () => Int) limit: number, + @Arg('cursor', () => String, { nullable: true }) cursor: string | null + ): Promise { + + // DESC: latest to oldest; ASC: oldest to latest + const realLimit = Math.min(limit, 50) + const realLimitPlusOne = realLimit + 1 + + const general = !spaceId && !pageId && !userId + let subscribedSpaceIds + let followedPageIds + let followedUserIds + if (general) { + const SpaceSubscriptions = await SpaceSubscription.findBy({ userId: req.session.userId }) + const PageFollows = await PageFollow.findBy({ userId: req.session.userId }) + const UserFollows = await UserFollow.findBy({ followingUserId: req.session.userId }) + subscribedSpaceIds = SpaceSubscriptions.map(ss => ss.spaceId) + followedPageIds = PageFollows.map(pf => pf.pageId) + followedUserIds = UserFollows.map(uf => uf.followedUserId) + } + + const offers: Offer[] = await orm.query(` + SELECT * + FROM offer o + ${cursor ? `WHERE o."createdAt" < '${toPostgresTime(cursor)}'` : 'WHERE true'} + ${spaceId ? `AND o."spaceId" = ${spaceId}` : ''} + ${pageId ? `AND o."pageCreatorId" = ${pageId}` : ''} + ${userId ? `AND o."creatorId" = ${userId}` : ''} + ${general && req.session.userId ? ` + AND ( + ${subscribedSpaceIds && subscribedSpaceIds.length ? `o."spaceId" IN (${subscribedSpaceIds.join(', ')})` : 'false'} + ${followedPageIds && followedPageIds.length ? `OR o."pageCreatorId" IN (${followedPageIds.join(', ')})` : 'OR false'} + ${followedUserIds && followedUserIds.length ? `OR o."creatorId" IN (${followedUserIds.join(', ')})` : 'OR false'} + OR o."creatorId" = ${req.session.userId} + ) + ` : '' + } + ORDER BY + ${req.session.userId ? `o."creatorId" = ${req.session.userId} DESC,` : ''} + o."createdAt" DESC + LIMIT ${realLimitPlusOne} + `) + + return { + offers: offers.slice(0, realLimit), + hasMore: offers.length - 1 == realLimit + } + } + + @Query(() => Offer, { nullable: true }) + async offer( + @Arg('id', () => Int) id: number, + ): Promise { + + return await Offer.findOneBy({ id }) + } + + @Mutation(() => OfferResponse) + @UseMiddleware(isAuth) + async createOffer( + @Ctx() { orm, req }: Context, + @Arg('input', () => OfferInputCreate) input: OfferInputCreate, + ): Promise { + const { + pageId, + spaceName, + title, + address, + benefits, + department, + description, + employmentType, + recruiting, + requirements, + salaryRange, + workplace, + photo + } = input + + if (!spaceName) { + return { + errors: [{ + field: 'spaceName', + message: 'Space cannot be empty.' + }] + } + } + + if (!title) { + return { + errors: [{ + field: 'title', + message: 'Title is required.' + }] + } + } + + const space = await Space.findOne({ where: { spaceName } }) + if (!space) { + return { + errors: [{ + field: 'spaceName', + message: 'Space does not exist!' + }] + } + } + + if (pageId) { + const page = await Page.findOne({ where: { id: pageId } }) + if (!page) { + return { + errors: [{ + field: 'pageId', + message: 'Page does not exist!' + }] + } + } + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${pageId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + } + else { + const subscription = await SpaceSubscription.findOne({ where: { spaceId: space.id, userId: req.session.userId } }) + if (!subscription) { + return { + errors: [{ + field: 'spaceName', + message: 'You are not subscribed to this space!' + }] + } + } + } + const offer = await Offer.create({ + spaceId: space.id, + creatorType: pageId ? 'page' : 'user', + creatorId: req.session.userId, + pageCreatorId: pageId, + title, + address, + benefits, + department, + description, + employmentType, + recruiting, + requirements, + salaryRange, workplace, + }).save() + if (photo) { + const { createReadStream, filename } = await photo + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `offers/${offer.id}/${v4()}.${ext}`, + }).promise() + offer.photo = uploaded.Key + await offer.save() + } + + return { + offer + } + } + + @Mutation(() => Offer, { nullable: true }) + @UseMiddleware(isAuth) + async updateOffer( + @Arg('id', () => Int) id: number, + @Arg('input', () => OfferInputUpdate) input: OfferInputUpdate, + @Ctx() { orm, req }: Context + ): Promise { + const { photo } = input + const offer = await Offer.findOne({ where: { id } }) + if (offer) { + if (offer.creatorType == 'user') { + if (offer.creatorId != req.session.userId) { + throw new Error('Not authorised!') + } + } + else { + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${offer.pageCreatorId}; + `) + if (ownerProof.length == 0) { + throw new Error('Not authorised!') + } + } + saveAttributes(offer, input) + if (photo) { + const { createReadStream, filename, } = await photo + const ext = getExtensionFromFilename(filename) + const stream = createReadStream() + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `offers/${offer.id}/${v4()}.${ext}`, + }).promise() + offer.photo = uploaded.Key + } + return await offer.save() + } + else { + throw new Error('Offer not found.') + } + } + + @Mutation(() => Boolean, { nullable: true }) + @UseMiddleware(isAuth) + async deleteOffer( + @Arg('id', () => Int) id: number, + @Ctx() { orm, req }: Context + ): Promise { + const offer = await Offer.findOneBy({ id }) + if (!offer) { + return false + } + if (offer.creatorType == 'user') { + if (offer.creatorId != req.session.userId) { + throw new Error('Not authorised!') + } + } + else { + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${offer.pageCreatorId}; + `) + if (ownerProof.length == 0) { + throw new Error('Not authorised!') + } + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `offers/${offer.id}` }).promise() + result?.Contents?.forEach(async c => { + if (c.Key) { + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: c.Key }).promise() + } + }) + await offer.remove() + return true + } + + @Mutation(() => Boolean) + triggerOffersInvalidate() { + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => OfferApplication) + async apply( + @Arg('offerId', () => Int) offerId: number, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const offer = await Offer.findOne({ where: { id: offerId } }) + if (!offer) { + throw new Error('Offer not found.') + } + if (offer.creatorId == userId) { + throw new Error('You cannot apply to your own offer.') + } + if (!offer.recruiting) { + throw new Error('Offer is not recruiting.') + } + const application = await OfferApplication.findOneBy({ offerId, userId }) + if (application) { + throw new Error('You have already applied to this offer.') + } + return OfferApplication.create({ + offerId, + userId: req.session.userId + }).save() + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteOfferApplication( + @Arg('offerId', () => Int) offerId: number, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const application = await OfferApplication.findOneBy({ offerId, userId }) + if (application) { + if (application.userId == userId) { + await application.remove() + return true + } + else { + throw new Error('Not authorised!') + } + } + return false + } + + @UseMiddleware(isAuth) + @Mutation(() => OfferApplication) + async updateApplicationStatus( + @Arg('id', () => Int) id: number, + @Arg('status', () => String) status: 'accepted' | 'rejected', + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const application = await OfferApplication.findOne({ where: { id } }) + if (!application) { + throw new Error('Application not found.') + } + const offer = (await Offer.findOne({ where: { id: application.offerId } })) as Offer + if (offer.creatorType == 'user') { + if (offer.creatorId != userId) { + throw new Error('Not authorised!') + } + } + else { + const page = await Page.findOne({ where: { id: offer.pageCreatorId }, relations: { owners: true } }) + if (!page?.owners?.map(o => o.id).includes(userId!)) { + throw new Error('Not authorised!') + } + } + if (!['accepted', 'rejected'].includes(status)) { + throw new Error('Invalid status.') + } + application.status = status + return application.save() + } + + @UseMiddleware(isAuth) + @Query(() => [Offer]) + async appliedOffers( + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const applications = await OfferApplication.find({ where: { userId } }) + const offerIds = applications.map(a => a.offerId) + return await Offer.find({ where: { id: In(offerIds) } }) + } +} \ No newline at end of file diff --git a/src/resolvers/page.ts b/src/resolvers/page.ts new file mode 100644 index 0000000..ca54259 --- /dev/null +++ b/src/resolvers/page.ts @@ -0,0 +1,360 @@ +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { Page, PageFollow, User } from '@entities' +import { Context } from '@types' +import { isAuth } from '@middlewares' +import { In } from 'typeorm' +import { s3 } from '@configs' +import { PageInfo, PageResponse, PhotoResponse, UploadedFileResponse } from '@graphql-types' +import _ from 'lodash' +import { GraphQLUpload, FileUpload } from 'graphql-upload' +import { v4 } from 'uuid' +import { getExtensionFromFilename, saveAttributes } from '@utils' + +@Resolver(Page) +export class PageResolver { + @FieldResolver(() => String) + avatarUrl( + @Root() page: Page + ): Promise { + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: page.avatar }) + } + + @FieldResolver(() => String) + coverPhotoUrl( + @Root() page: Page + ): Promise { + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: page.coverPhoto }) + } + + @Query(() => Boolean, { nullable: true }) + @FieldResolver(() => Boolean, { nullable: true }) + async followStatus( + @Root() root: Page, + @Arg('pageId', () => Int, { nullable: true }) pageId: number, + @Ctx() { req, followLoader }: Context + ): Promise { + const { userId } = req.session + if (pageId) { + return userId ? + followLoader.load({ pageId, userId }) + : + null + } + else { + return userId ? + followLoader.load({ pageId: root.id, userId }) + : + null + } + } + + @FieldResolver(() => Boolean, { nullable: true }) + async ownerStatus( + @Root() root: Page, + @Ctx() { req, ownerStatusLoader }: Context + ): Promise { + const { userId } = req.session + return userId ? + ownerStatusLoader.load({ pageId: root.id, userId }) + : + null + } + + @FieldResolver(() => Int) + async followerNumber( + @Root() page: Page, + @Ctx() { followerNumberLoader }: Context + ): Promise { + return followerNumberLoader.load(page.id) + } + + @Query(() => Page) + async page( + @Arg('pageName', () => String) pageName: string, + ): Promise { + return Page.findOneBy({ pageName }) + } + + @UseMiddleware(isAuth) + @Query(() => [Page]) + async myPages( + @Ctx() { orm, req }: Context + ): Promise { + const myPageIds: { pageId: number }[] = await orm.query(` + SELECT "pageId" FROM page_owners_user WHERE "userId" = ${req.session.userId}; + `) + return Page.findBy({ id: In(myPageIds.map(item => item.pageId)) }) + } + + @UseMiddleware(isAuth) + @Mutation(() => PageResponse) + async createPage( + @Arg('pageName') pageName: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const p = await Page.findOne({ where: { pageName } }) + if (p) { + return { + errors: [{ + field: 'pageName', + message: 'Page already exists.' + }] + } + } + if (!/^[A-Za-z0-9_-]*$/.test(pageName)) { + return { + errors: [{ + field: 'pageName', + message: 'Page\'s name must contain only letters, numbers, underscores and dashes.' + }] + } + } + const page = await Page.create({ + pageName, + owners: [(await User.findOneBy({ id: userId }))!], + }).save() + await PageFollow.create({ pageId: page.id, userId }).save() + return { page } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deletePage( + @Arg('id', () => Int) id: number, + @Ctx() { orm, req }: Context + ): Promise { + const page = await Page.findOne({ where: { id } }) + if (!page) { + throw new Error('Page not found.') + } + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${id}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + try { + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `pages/${id}` }).promise() + result?.Contents?.forEach(async c => { + if (c.Key) { + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: c.Key }).promise() + } + }) + await page.remove() + return true + } + catch (e) { + console.error(e) + return false + } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async follow( + @Arg('pageId', () => Int) pageId: number, + @Ctx() { orm, req }: Context + ) { + const { userId } = req.session + + const follow = await PageFollow.findOne({ where: { pageId, userId } }) + if (!follow) { + const page = await Page.findOne({ where: { id: pageId } }) + if (!page) { + return false + } + await PageFollow.create({ pageId, userId }).save() + } + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async unfollow( + @Arg('pageId', () => Int) pageId: number, + @Ctx() { req }: Context + ) { + const { userId } = req.session + + const follow = await PageFollow.findOne({ where: { pageId, userId } }) + if (follow) { + await follow.remove() + } + return true + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadPageAvatar( + @Arg('pageId', () => Int) pageId: number, + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${pageId} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `pages/${pageId}/photos/avatars/${v4()}.${ext}`, + }).promise() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async changePageAvatar( + @Arg('pageId', () => Int) pageId: number, + @Arg('key', () => String) key: string, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${pageId} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + await Page.update({ id: pageId }, { avatar: key }) + return true + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadPageCoverPhoto( + @Arg('pageId', () => Int) pageId: number, + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${pageId} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `pages/${pageId}/photos/coverPhotos/${v4()}.${ext}`, + }).promise() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async changePageCoverPhoto( + @Arg('pageId', () => Int) pageId: number, + @Arg('key', () => String) key: string, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${pageId} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + await Page.update({ id: pageId }, { coverPhoto: key }) + return true + } + + @Query(() => [PhotoResponse]) + @UseMiddleware(isAuth) + async pageAvatars( + @Arg('pageId', () => Int) pageId: number, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${pageId} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `pages/${pageId}/photos/avatars` }).promise() + let keys = result?.Contents?.map(item => item.Key as string) + if (keys == undefined || keys.length == 0) { + return [] + } + else { + keys = keys.filter(k => k != `pages/${pageId}/photos/avatars`) + return Promise.all(keys.map(async key => { + return { + key, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } as PhotoResponse + })) + } + } + + @Query(() => [PhotoResponse]) + @UseMiddleware(isAuth) + async pageCoverPhotos( + @Arg('pageId', () => Int) pageId: number, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${pageId} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `pages/${pageId}/photos/coverPhotos` }).promise() + let keys = result?.Contents?.map(item => item.Key as string) + if (keys == undefined || keys.length == 0) { + return [] + } + else { + keys = keys.filter(k => k != `pages/${pageId}/photos/coverPhotos`) + return Promise.all(keys.map(async key => { + return { + key, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } as PhotoResponse + })) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async updatePageInfo( + @Arg('id', () => Int) id: number, + @Arg('input', () => PageInfo) input: PageInfo, + @Ctx() { orm, req }: Context + ): Promise { + const { userId } = req.session + const ownerProof = await orm.query(` + SELECT * FROM page_owners_user WHERE "pageId" = ${id} AND "userId" = ${userId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + const page = await Page.findOneBy({ id }) + saveAttributes(page, input) + await page!.save() + return true + } +} \ No newline at end of file diff --git a/src/resolvers/post.ts b/src/resolvers/post.ts new file mode 100644 index 0000000..65d403a --- /dev/null +++ b/src/resolvers/post.ts @@ -0,0 +1,333 @@ +import { Page, PageFollow, Post, Space, SpaceSubscription, Tag, User, UserFollow, Vote } from '@entities' +import { PaginatedPosts, PostInputCreate, PostInputUpdate, PostResponse } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { saveAttributes, toPostgresTime } from '@utils' +import _ from 'lodash' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { In } from 'typeorm' + +@Resolver(Post) +export class PostResolver { + + @FieldResolver(() => User) + creator( + @Root() root: Post, + @Ctx() { userLoader }: Context + ) { + return root.creatorId ? userLoader.load(root.creatorId) : null + } + + @FieldResolver(() => Page) + pageCreator( + @Root() root: Post, + @Ctx() { pageLoader }: Context + ) { + return root.pageCreatorId ? pageLoader.load(root.pageCreatorId) : null + } + + @FieldResolver(() => Space) + space( + @Root() root: Post, + @Ctx() { spaceLoader }: Context + ) { + return spaceLoader.load(root.spaceId) + } + + @FieldResolver(() => Int, { nullable: true }) + async voteStatus( + @Root() root: Post, + @Ctx() { req, voteLoader }: Context + ) { + return req.session?.userId ? + (await voteLoader.load({ postId: root.id, userId: req.session.userId }))?.value + : + null + } + + @FieldResolver(() => [Tag]) + async tags( + @Root() root: Post, + @Ctx() { tagLoader }: Context + ): Promise { + return await tagLoader.load(root.id) + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async vote( + @Arg('postId', () => Int) postId: number, + @Arg('value', () => Int) value: number, + @Ctx() { req, orm }: Context + ) { + const { userId } = req.session + if (value == 0) return false + const realValue = value > 0 ? 1 : value < 0 ? -1 : 0 + + const vote = await Vote.findOne({ where: { postId, userId } }) + + if (vote) { + if (vote.value == realValue) { + await vote.remove() + const post = await Post.findOne({ where: { id: postId } }) + if (post) { + post.points = post.points + (realValue == 1 ? -1 : 1) + await post.save() + } + } + else { + await orm.transaction(async transactionalEntityManager => { + vote.value = realValue + await transactionalEntityManager.save(vote) + await transactionalEntityManager.increment(Post, { id: postId }, 'points', realValue == -1 ? -2 : 2) + }) + } + } + else { + await orm.transaction(async transactionalEntityManager => { + const newVote = new Vote() + newVote.userId = userId! + newVote.postId = postId + newVote.value = realValue + await transactionalEntityManager.save(newVote) + await transactionalEntityManager.increment(Post, { id: postId }, 'points', realValue) + }) + } + + return true + } + + @Query(() => PaginatedPosts) + async posts( + @Ctx() { req, orm }: Context, + @Arg('spaceId', () => Int, { nullable: true }) spaceId: number, + @Arg('pageId', () => Int, { nullable: true }) pageId: number, + @Arg('userId', () => Int, { nullable: true }) userId: number, + @Arg('limit', () => Int) limit: number, + @Arg('cursor', () => String, { nullable: true }) cursor: string | null + ): Promise { + + // DESC: latest to oldest; ASC: oldest to latest + const realLimit = Math.min(limit, 50) + const realLimitPlusOne = realLimit + 1 + + const general = !spaceId && !pageId && !userId + let subscribedSpaceIds + let followedPageIds + let followedUserIds + if (general) { + const SpaceSubscriptions = await SpaceSubscription.findBy({ userId: req.session.userId }) + const PageFollows = await PageFollow.findBy({ userId: req.session.userId }) + const UserFollows = await UserFollow.findBy({ followingUserId: req.session.userId }) + subscribedSpaceIds = SpaceSubscriptions.map(ss => ss.spaceId) + followedPageIds = PageFollows.map(pf => pf.pageId) + followedUserIds = UserFollows.map(uf => uf.followedUserId) + } + + const posts: Post[] = await orm.query(` + SELECT * + FROM post p + ${cursor ? `WHERE p."createdAt" < '${toPostgresTime(cursor)}'` : 'WHERE true'} + ${spaceId ? `AND p."spaceId" = ${spaceId}` : ''} + ${pageId ? `AND p."pageCreatorId" = ${pageId}` : ''} + ${userId ? `AND p."creatorId" = ${userId}` : ''} + ${general && req.session.userId ? ` + AND ( + ${subscribedSpaceIds && subscribedSpaceIds.length ? `p."spaceId" IN (${subscribedSpaceIds.join(', ')})` : 'false'} + ${followedPageIds && followedPageIds.length ? `OR p."pageCreatorId" IN (${followedPageIds.join(', ')})` : 'OR false'} + ${followedUserIds && followedUserIds.length ? `OR p."creatorId" IN (${followedUserIds.join(', ')})` : 'OR false'} + OR p."creatorId" = ${req.session.userId} + ) + ` : '' + } + ORDER BY + ${req.session.userId ? `p."creatorId" = ${req.session.userId} DESC,` : ''} + p.points DESC, + p."createdAt" DESC + LIMIT ${realLimitPlusOne} + `) + + return { + posts: posts.slice(0, realLimit), + hasMore: posts.length - 1 == realLimit + } + } + + @Query(() => Post, { nullable: true }) + async post( + @Arg('id', () => Int) id: number, + ): Promise { + + return await Post.findOne({ where: { id } }) + } + + @Mutation(() => PostResponse) + @UseMiddleware(isAuth) + async createPost( + @Ctx() { orm, req }: Context, + @Arg('input', () => PostInputCreate) input: PostInputCreate, + ): Promise { + const { spaceName, title, text, tags, pageId } = input + + if (!spaceName) { + return { + errors: [{ + field: 'spaceName', + message: 'Space cannot be empty.' + }] + } + } + + if (!title) { + return { + errors: [{ + field: 'title', + message: 'Title is required.' + }] + } + } + + if (tags == null || tags.length == 0) { + return { + errors: [{ + field: 'tags', + message: 'All posts must have at least one tag.' + }] + } + } + + const space = await Space.findOne({ where: { spaceName } }) + if (!space) { + return { + errors: [{ + field: 'spaceName', + message: 'Space does not exist!' + }] + } + } + + if (pageId) { + const page = await Page.findOne({ where: { id: pageId } }) + if (!page) { + return { + errors: [{ + field: 'pageId', + message: 'Page does not exist!' + }] + } + } + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${pageId}; + `) + if (ownerProof.length == 0) { + throw new Error('You are not an owner of this page.') + } + } + else { // pages don't have to subsrcibe to space to create posts/offers + const subscription = await SpaceSubscription.findOne({ where: { spaceId: space.id, userId: req.session.userId } }) + if (!subscription) { + return { + errors: [{ + field: 'spaceName', + message: 'You are not subscribed to this space!' + }] + } + } + } + + const dbTags = await Tag.find({ where: { id: In(tags) } }) + if (dbTags.length != tags.length) { + return { + errors: [{ + field: 'tags', + message: 'One or more tags do not exist!' + }] + } + } + + return { + post: await Post.create({ + title, + text, + spaceId: space.id, + creatorType: pageId ? 'page' : 'user', + creatorId: pageId ? undefined : req.session.userId, + pageCreatorId: pageId, + tags: dbTags + }).save() + } + } + + @Mutation(() => PostResponse, { nullable: true }) + @UseMiddleware(isAuth) + async updatePost( + @Arg('id', () => Int) id: number, + @Arg('input', () => PostInputUpdate) input: PostInputUpdate, + @Ctx() { orm, req }: Context + ): Promise { + type FormattedInput = Omit & { tags?: Tag[] } + let clone = _.cloneDeep(input) + delete clone.tags + let formattedInput: FormattedInput = clone as Omit + if (input.tags && input.tags.length > 0) { + formattedInput.tags = await Tag.find({ where: { id: In(input.tags!) } }) + if (formattedInput.tags.length != input.tags.length) { + throw new Error('One or more tags do not exist!') + } + } + const post = await Post.findOne({ where: { id } }) + if (post) { + if (post.creatorType == 'user') { + if (post.creatorId != req.session.userId) { + throw new Error('Not authorised!') + } + } + else { + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${post.pageCreatorId}; + `) + if (ownerProof.length == 0) { + throw new Error('Not authorised!') + } + } + saveAttributes(post, formattedInput) + await post.save() + return { post } + } + else { + throw new Error('Post not found.') + } + } + + @Mutation(() => Boolean, { nullable: true }) + @UseMiddleware(isAuth) + async deletePost( + @Arg('id', () => Int) id: number, + @Ctx() { orm, req }: Context + ): Promise { + const post = await Post.findOneBy({ id }) + if (!post) { + return false + } + if (post.creatorType == 'user') { + if (post.creatorId != req.session.userId) { + throw new Error('Not authorised!') + } + } + else { + const ownerProof: { userId: number, pageId: number }[] = await orm.query(` + SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${post.pageCreatorId}; + `) + if (ownerProof.length == 0) { + throw new Error('Not authorised!') + } + } + await post.remove() + return true + } + + @Mutation(() => Boolean) + triggerPostsInvalidate() { + return true + } +} \ No newline at end of file diff --git a/src/resolvers/qualification.ts b/src/resolvers/qualification.ts new file mode 100644 index 0000000..9e5993f --- /dev/null +++ b/src/resolvers/qualification.ts @@ -0,0 +1,137 @@ +import { s3 } from '@configs' +import { Qualification } from '@entities' +import { QualificationInput, QualificationResponse } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { getExtensionFromFilename, saveAttributes } from '@utils' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { v4 } from 'uuid' + +@Resolver(Qualification) +export class QualificationResolver { + @FieldResolver(() => String) + async photoUrl( + @Root() item: Qualification + ): Promise { + return item.photo ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: item.photo }) : null + } + + @Query(() => [Qualification]) + async qualifications( + @Arg('userId', () => Int) userId: number + ): Promise { + return (await Qualification.find({ where: { userId } })) + } + + @UseMiddleware(isAuth) + @Mutation(() => QualificationResponse) + async createQualification( + @Arg('input', () => QualificationInput) input: QualificationInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { name, issuingOrganisation, issuanceDate, expire, expirationDate, photo, credentialID, credentialURL } = input + if (!name) { + return { + errors: [{ + field: 'name', + message: 'Name cannot be empty!' + }] + } + } + if (!issuingOrganisation) { + return { + errors: [{ + field: 'issuingOrganisation', + message: 'Issuing organisation cannot be empty!' + }] + } + } + let key + if (photo) { + const { createReadStream, filename } = await photo + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/qualifications/${v4()}.${ext}`, + }).promise() + key = uploaded.Key + } + return { + qualification: await Qualification.create({ + name, + issuingOrganisation, + issuanceDate, + expire, + expirationDate, + credentialID, + credentialURL, + photo: key, + userId + }).save() + } + } + + @UseMiddleware(isAuth) + @Mutation(() => QualificationResponse) + async updateQualification( + @Arg('id', () => Int) id: number, + @Arg('input', () => QualificationInput) input: QualificationInput, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { name, issuingOrganisation, photo } = input + if (!name) { + return { + errors: [{ + field: 'name', + message: 'Name cannot be empty!' + }] + } + } + if (!issuingOrganisation) { + return { + errors: [{ + field: 'issuingOrganisation', + message: 'Issuing organisation cannot be empty!' + }] + } + } + const qualification = await Qualification.findOne({ where: { id, userId } }) + if (!qualification) { + throw new Error('Not authorised!') + } + saveAttributes(qualification, input) + if (photo) { + const { createReadStream, filename, } = await photo + const ext = getExtensionFromFilename(filename) + const stream = createReadStream() + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/qualifications/${v4()}.${ext}`, + }).promise() + qualification.photo = uploaded.Key + } + return { + qualification: await qualification.save() + } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteQualification( + @Arg('id', () => Int) id: number, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const qualification = await Qualification.findOne({ where: { id, userId } }) + if (!qualification) { + throw new Error('Not authorised!') + } + await qualification.remove() + return true + } +} \ No newline at end of file diff --git a/src/resolvers/quicksight.ts b/src/resolvers/quicksight.ts new file mode 100644 index 0000000..1c77c12 --- /dev/null +++ b/src/resolvers/quicksight.ts @@ -0,0 +1,20 @@ +import { quicksight } from '@configs' +import { Query, Resolver } from 'type-graphql' + +@Resolver() +export class QuicksightResolver { + @Query(() => String) + async embedUrl(): Promise { + const resp = await quicksight.generateEmbedUrlForAnonymousUser({ + AwsAccountId: process.env.AWS_ACCOUNT_ID, + Namespace: 'default', + AuthorizedResourceArns: [`arn:aws:quicksight:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:dashboard/${process.env.QUICKSIGHT_DASHBOARD_ID}`], + ExperienceConfiguration: { + Dashboard: { + InitialDashboardId: process.env.QUICKSIGHT_DASHBOARD_ID + } + } + }).promise() + return resp.EmbedUrl! + } +} \ No newline at end of file diff --git a/src/resolvers/s3.ts b/src/resolvers/s3.ts new file mode 100644 index 0000000..5855a2e --- /dev/null +++ b/src/resolvers/s3.ts @@ -0,0 +1,20 @@ +import { s3 } from '@configs' +import { Arg, Query, Resolver } from 'type-graphql' + +@Resolver() +export class S3Resolver { + @Query(() => String, { nullable: true }) + getSignedUrl( + @Arg('key') key: string + ): Promise { + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } + + @Query(() => [String]) + getSignedUrls( + @Arg('keys', () => [String]) keys: string[] + ): Promise { + return Promise.all(keys.map(async key => + await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }))) + } +} \ No newline at end of file diff --git a/src/resolvers/space.ts b/src/resolvers/space.ts new file mode 100644 index 0000000..e0b845e --- /dev/null +++ b/src/resolvers/space.ts @@ -0,0 +1,391 @@ +import { s3 } from '@configs' +import { Space, SpaceSubscription, User } from '@entities' +import { PhotoResponse, SpaceInfo, SpaceResponse, UploadedFileResponse } from '@graphql-types' +import { isAuth } from '@src/middlewares' +import { Context } from '@types' +import { getExtensionFromFilename, saveAttributes } from '@utils' +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { In } from 'typeorm' +import { v4 } from 'uuid' + +@Resolver(Space) +export class SpaceResolver { + + @FieldResolver(() => String) + async avatarUrl( + @Root() space: Space + ): Promise { + return space.avatar ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: space.avatar }) : null + } + + @FieldResolver(() => String) + async coverPhotoUrl( + @Root() space: Space + ): Promise { + return space.avatar ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: space.coverPhoto }) : null + } + + @Query(() => Boolean, { nullable: true }) + @FieldResolver(() => Boolean, { nullable: true }) + async subscriptionStatus( + @Root() root: Space, + @Arg('spaceId', () => Int, { nullable: true }) spaceId: number, + @Ctx() { req, subscriptionLoader }: Context + ): Promise { + if (spaceId) { + return req.session?.userId ? + subscriptionLoader.load({ spaceId, userId: req.session.userId }) + : + null + } + else { + return req.session?.userId ? + subscriptionLoader.load({ spaceId: root.id, userId: req.session.userId }) + : + null + } + } + + @FieldResolver(() => Boolean) + async modStatus( + @Root() root: Space, + @Ctx() { req, modStatusLoader }: Context + ): Promise { + return req.session?.userId ? + modStatusLoader.load({ spaceId: root.id, userId: req.session.userId }) + : + false + } + + @FieldResolver(() => Int) + async subscriberNumber( + @Root() space: Space, + @Ctx() { subscriberNumberLoader }: Context + ): Promise { + return subscriberNumberLoader.load(space.id) + } + + @UseMiddleware(isAuth) + @Query(() => [Space]) + async mySpaces( + @Ctx() { orm, req }: Context + ): Promise { + const mySpaceIds: { spaceId: number }[] = await orm.query(` + SELECT "spaceId" FROM space_mods_user WHERE "userId" = ${req.session.userId}; + `) + return Space.findBy({ id: In(mySpaceIds.map(item => item.spaceId)) }) + } + + @Mutation(() => SpaceResponse) + @UseMiddleware(isAuth) + async createSpace( + @Arg('spaceName', () => String) spaceName: string, + @Ctx() { req }: Context + ): Promise { + const s = await Space.findOne({ where: { spaceName } }) + if (s) { + return { + errors: [{ + field: 'spaceName', + message: 'Space already exists.' + }] + } + } + if (!/^[A-Za-z0-9_-]*$/.test(spaceName)) { + return { + errors: [{ + field: 'spaceName', + message: 'Space\'s name must contain only letters, numbers, underscores and dashes.' + }] + } + } + const space = Space.create({ spaceName }) + space.mods = [(await User.findOneBy({ id: req.session.userId }))!] + await space.save() + await SpaceSubscription.create({ spaceId: space.id, userId: req.session.userId }).save() + return { space } + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadSpaceAvatar( + @Arg('spaceId', () => Int) spaceId: number, + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `spaces/${spaceId}/photos/avatars/${v4()}.${ext}`, + }).promise() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async changeSpaceAvatar( + @Arg('spaceId', () => Int) spaceId: number, + @Arg('key', () => String) key: string, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + await Space.update({ id: spaceId }, { avatar: key }) + return true + } + + @UseMiddleware(isAuth) + @Query(() => [PhotoResponse]) + async spaceAvatars( + @Arg('spaceId', () => Int) spaceId: number, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `spaces/${spaceId}/photos/avatars` }).promise() + let keys = result?.Contents?.map(item => item.Key as string) + if (keys == undefined || keys.length == 0) { + return [] + } + else { + keys = keys.filter(k => k != `spaces/${spaceId}/photos/avatars`) + return Promise.all(keys.map(async key => { + return { + key, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } as PhotoResponse + })) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async deleteSpaceAvatar( + @Arg('key', () => String) key: string, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const regex = new RegExp('(?<=spaces/)(.*)(?=/photos/avatars)') + const spaceId = regex.exec(key)?.[0] + if (!spaceId) { + throw new Error('Invalid key.') + } + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: key }).promise() + return true + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadSpaceCoverPhoto( + @Arg('spaceId', () => Int) spaceId: number, + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `spaces/${spaceId}/photos/coverPhotos/${v4()}.${ext}`, + }).promise() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async changeSpaceCoverPhoto( + @Arg('spaceId', () => Int) spaceId: number, + @Arg('key', () => String) key: string, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + await Space.update({ id: spaceId }, { coverPhoto: key }) + return true + } + + @Query(() => [PhotoResponse]) + @UseMiddleware(isAuth) + async spaceCoverPhotos( + @Arg('spaceId', () => Int) spaceId: number, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `spaces/${spaceId}/photos/coverPhotos` }).promise() + let keys = result?.Contents?.map(item => item.Key as string) + if (keys == undefined || keys.length == 0) { + return [] + } + else { + keys = keys.filter(k => k != `spaces/${spaceId}/photos/coverPhotos`) + return Promise.all(keys.map(async key => { + return { + key, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } as PhotoResponse + })) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async deleteSpaceCoverPhoto( + @Arg('key', () => String) key: string, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const regex = new RegExp('(?<=spaces/)(.*)(?=/photos/coverPhotos)') + const spaceId = regex.exec(key)?.[0] + if (!spaceId) { + throw new Error('Invalid key.') + } + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${spaceId} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: key }).promise() + return true + } + + @Query(() => Space, { nullable: true }) + async space( + @Arg('spaceName', () => String) spaceName: string + ): Promise { + return Space.findOne({ where: { spaceName } }) + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async subscribe( + @Arg('spaceId', () => Int) spaceId: number, + @Ctx() { req }: Context + ) { + const { userId } = req.session + + const subscription = await SpaceSubscription.findOne({ where: { spaceId, userId } }) + if (!subscription) { + const space = await Space.findOne({ where: { id: spaceId } }) + if (!space) { + return false + } + await SpaceSubscription.create({ spaceId, userId }).save() + } + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async unsubscribe( + @Arg('spaceId', () => Int) spaceId: number, + @Ctx() { req }: Context + ) { + const { userId } = req.session + + const subscription = await SpaceSubscription.findOne({ where: { spaceId, userId } }) + if (subscription) { + await subscription.remove() + } + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async deleteSpace( + @Arg('id', () => Int) id: number, + @Ctx() { req, orm }: Context + ): Promise { + const { userId } = req.session + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${id} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `spaces/${id}` }).promise() + result?.Contents?.forEach(async c => { + if (c.Key) { + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: c.Key }).promise() + } + }) + await Space.delete({ id }) + return true + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async updateSpaceInfo( + @Arg('id', () => Int) id: number, + @Arg('input', () => SpaceInfo) input: SpaceInfo, + @Ctx() { orm, req }: Context + ): Promise { + const { userId } = req.session + const modProof = await orm.query(` + SELECT * FROM space_mods_user WHERE "spaceId" = ${id} AND "userId" = ${userId}; + `) + if (modProof.length == 0) { + throw new Error('You are not a mod of this space.') + } + const space = await Space.findOneBy({ id }) + saveAttributes(space, input) + await space!.save() + return true + } +} \ No newline at end of file diff --git a/src/resolvers/tag.ts b/src/resolvers/tag.ts new file mode 100644 index 0000000..dd402bc --- /dev/null +++ b/src/resolvers/tag.ts @@ -0,0 +1,10 @@ +import { Query, Resolver } from 'type-graphql' +import { Tag } from '@entities' + +@Resolver(Tag) +export class TagResolver { + @Query(() => [Tag], { nullable: true }) + async tags(): Promise { + return await Tag.find() + } +} \ No newline at end of file diff --git a/src/resolvers/user.ts b/src/resolvers/user.ts new file mode 100644 index 0000000..ccefab6 --- /dev/null +++ b/src/resolvers/user.ts @@ -0,0 +1,542 @@ +import { ConversationCol, InboxCol } from '@collections' +import { s3 } from '@configs' +import { COOKIE_NAME, FORGET_PASSWORD_PREFIX, __prod__ } from '@constants' +import { Vote, User, UserFollow, Conversation as ConversationEnt } from '@entities' +import { ForgotPasswordResponse, Location, PhotoResponse, UploadedFileResponse, UserInfo, UserResponse } from '@graphql-types' +import { isAuth } from '@middlewares' +import { Context } from '@types' +import { getExtensionFromFilename, isValidEmail, saveAttributes, sendEmail, validateLogin, validateRegister } from '@utils' +import argon2 from 'argon2' +import { FileUpload, GraphQLUpload } from 'graphql-upload' +import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql' +import { ArrayContains } from 'typeorm' +import { v4 } from 'uuid' + +@Resolver(User) +export class UserResolver { + + @Query(() => Boolean, { nullable: true }) + @FieldResolver(() => Boolean, { nullable: true }) + async userFollowStatus( + @Root() root: User, + @Arg('id', () => Int, { nullable: true }) id: number, + @Ctx() { req, userFollowLoader }: Context + ): Promise { + const { userId } = req.session + if (id) { + return userId ? + userFollowLoader.load({ followedUserId: id, followingUserId: userId }) + : + null + } + else { + return userId ? + userFollowLoader.load({ followedUserId: root.id, followingUserId: userId }) + : + null + } + } + + @Query(() => String, { nullable: true }) + @FieldResolver(() => String) + async avatarUrl( + @Root() user: User, + @Ctx() { req }: Context + ): Promise { + if (user) { + if (user.avatar) { + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: user.avatar }) + } + return null + } + else { + const thisUser = await User.findOneBy({ id: req.session.userId }) + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: thisUser!.avatar }) + } + } + + @Query(() => String, { nullable: true }) + @FieldResolver(() => String) + async coverPhotoUrl( + @Root() user: User, + @Ctx() { req }: Context + ): Promise { + if (user) { + if (user.coverPhoto) { + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: user.coverPhoto }) + } + return null + } + else { + const thisUser = await User.findOneBy({ id: req.session.userId }) + return s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: thisUser!.coverPhoto }) + } + } + + @FieldResolver(() => Int) + async followerNumber( + @Root() user: User, + @Ctx() { userFollowerNumberLoader }: Context + ): Promise { + return userFollowerNumberLoader.load(user.id) + } + + @FieldResolver(() => Int) + async followingNumber( + @Root() user: User, + @Ctx() { userFollowingNumberLoader }: Context + ): Promise { + return userFollowingNumberLoader.load(user.id) + } + + @FieldResolver(() => String) + email( + @Root() user: User, + @Ctx() { req }: Context + ): string | null { + if (req.session.userId == user.id) { + return user.email! + } + return '' + } + + @Query(() => User, { nullable: true }) + async user( + @Arg('username', () => String) username: string, + ): Promise { + + return await User.findOne({ where: { username } }) + } + + @Mutation(() => UserResponse) + async changePassword( + @Arg('token') token: string, + @Arg('newPassword') newPassword: string, + @Ctx() { redis, req }: Context + ): Promise { + if (newPassword.length < 8) { + return { + errors: [{ + field: 'newPassword', + message: 'Password must be at least 8 characters.' + }] + } + } + if (newPassword.length > 128) { + return { + errors: [{ + field: 'newPassword', + message: 'Password must not be longer than 128 characters.' + }] + } + } + + const redisKey = FORGET_PASSWORD_PREFIX + token + + const userId = await redis.get(redisKey) + if (!userId) { + return { + errors: [{ + field: 'token', + message: 'Token invalid/expired.' + }] + } + } + const id = parseInt(userId) + const user = await User.findOneBy({ id }) + + if (!user) { + return { + errors: [{ + field: 'token', + message: 'User no longer exists.' + }] + } + } + + await User.update({ id }, { password: await argon2.hash(newPassword) }) + await redis.del(redisKey) + + req.session.userId = user.id + + return { user } + } + + @Mutation(() => ForgotPasswordResponse) + async forgotPassword( + @Arg('email') email: string, + @Ctx() { redis }: Context + ): Promise { + if (!isValidEmail(email)) { + return { + errors: [{ + field: 'email', + message: 'Invalid email.' + }], + success: false + } + } + + const user = await User.findOneBy({ email }) + if (!user) { + return { success: true } + } + + const token = v4() + await redis.set(FORGET_PASSWORD_PREFIX + token, user.id, 'EX', 1000 * 60 * 60 * 24 * 3) + + await sendEmail( + email, + `Reset Password` + ) + return { success: true } + } + + @Query(() => User, { nullable: true }) + async me(@Ctx() { req }: Context): Promise { + if (!req.session.userId) { + return null + } + return User.findOneBy({ id: req.session.userId }) + } + + @Mutation(() => UserResponse) + async register( + @Arg('email') email: string, + @Arg('username') username: string, + @Arg('password') password: string, + @Arg('location', () => Location, { nullable: true }) location: Location, + @Ctx() { orm, req }: Context + ): Promise { + const errors = validateRegister(email, username, password) + if (errors) { + return { errors } + } + const hashedPassword = await argon2.hash(password) + let user: User + try { + const result = await orm.createQueryBuilder().insert().into(User).values({ + email, + username, + password: hashedPassword, + mostRecentLatitude: location?.latitude, + mostRecentLongitude: location?.longitude + }).returning('*').execute() + user = result.raw[0] + } + catch (err) { + if (err.code == '23505') { + const erroneousField: string = err.detail.slice(err.detail.indexOf('(') + 1, err.detail.indexOf(')')) + return { + errors: [{ + field: erroneousField, + message: `${erroneousField.charAt(0).toUpperCase() + erroneousField.slice(1)} already taken.` + }] + } + } + else { + console.error(err) + throw new Error('Internal Server Error!') + } + } + req.session.userId = user!.id + return { user: user! } + } + + @Mutation(() => UserResponse) + async login( + @Arg('emailOrUsername') emailOrUsername: string, + @Arg('password') password: string, + @Arg('location', () => Location, { nullable: true }) location: Location, + @Ctx() ctx: Context + ): Promise { + const { req } = ctx + const { input, errors } = validateLogin(emailOrUsername, password) + if (errors) { + return { errors } + } + let user: User | null + try { + user = await User.findOneBy( + input == 'Email' ? { email: emailOrUsername } : { username: emailOrUsername } + ) + if (!user) { + return { + errors: [{ + field: 'emailOrUsername', + message: `${input} doesn\'t exist.` + }] + } + } + } + catch (err) { + return { + errors: [{ + field: 'emailOrUsername', + message: 'Internal Server Error!' + }] + } + } + const valid = await argon2.verify(user!.password!, password) + if (!valid) { + return { + errors: [{ + field: 'password', + message: 'Incorrect password!' + }] + } + } + req.session.userId = user.id + if (location) { + const { latitude, longitude } = location + await User.update({ id: user.id }, { mostRecentLatitude: latitude, mostRecentLongitude: longitude }) + } + return { user } + } + + @Mutation(() => Boolean) + logout(@Ctx() { req, res }: Context): Promise { + return new Promise((resolve) => { + req.session.destroy(err => { + res.clearCookie(COOKIE_NAME, + { + sameSite: __prod__ ? 'lax' : 'none', + secure: true, + } + ) + if (err) { + console.error(err) + resolve(false) + } + else { + resolve(true) + } + }) + }) + } + + @Mutation(() => Boolean, { nullable: true }) + @UseMiddleware(isAuth) + async deleteUser( + @Arg('id', () => Int) id: number, + @Ctx() { req, orm }: Context + ): Promise { + const user = await User.findOneBy({ id }) + if (!user) { + return false + } + if (user.id != req.session.userId) { + throw new Error('Not authorised!') + } + const votes = await Vote.findBy({ userId: id }) + const upvotes = votes.filter(v => v.value == 1) + const downvotes = votes.filter(v => v.value == -1) + const upvotedPostIds = upvotes.map(u => u.postId) + const downvotedPostIds = downvotes.map(d => d.postId) + if (upvotedPostIds.length > 0) { + orm.query(` + UPDATE post SET points = points - 1 WHERE id IN (${upvotedPostIds.join(', ')}); + `) + } + if (downvotedPostIds.length > 0) { + orm.query(` + UPDATE post SET points = points + 1 WHERE id IN (${downvotedPostIds.join(', ')}); + `) + } + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `users/${user.id}` }).promise() + result?.Contents?.forEach(async c => { + if (c.Key) { + await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: c.Key }).promise() + } + }) + const conversations = await ConversationEnt.findBy({ participants: ArrayContains([req.session.userId!]) }) + conversations.forEach(async c => { + await ConversationCol.delete(c.firestoreCollectionId) + const inboxes = await InboxCol.whereArrayContains('conversationIds', c.firestoreCollectionId).find() + inboxes.forEach(async i => { + i.conversationIds = i.conversationIds.filter(id => id != c.firestoreCollectionId) + await InboxCol.update(i) + }) + const inbox = await InboxCol.whereEqualTo('userId', req.session.userId!).findOne() + await InboxCol.delete(inbox!.id) + await c.remove() + }) + await user.remove() + return true + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async updateInfo( + @Arg('input', () => UserInfo) input: UserInfo, + @Ctx() { req }: Context + ): Promise { + const user = await User.findOneBy({ id: req.session.userId }) + saveAttributes(user, input) + await user!.save() + return true + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadAvatar( + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/avatars/${v4()}.${ext}`, + }).promise() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async changeAvatar( + @Arg('key', () => String) key: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + await User.update({ id: userId }, { avatar: key }) + return true + } + + @Mutation(() => UploadedFileResponse, { nullable: true }) + @UseMiddleware(isAuth) + async uploadCoverPhoto( + @Arg('upload', () => GraphQLUpload) upload: FileUpload, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const { createReadStream, filename, mimetype, encoding } = upload as FileUpload + const stream = createReadStream() + const ext = getExtensionFromFilename(filename) + const uploaded = await s3.upload({ + Bucket: process.env.S3_BUCKET, + Body: stream, + Key: `users/${userId}/photos/coverPhotos/${v4()}.${ext}`, + }).promise() + return { + filename, + mimetype, + encoding, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key }) + } + } + + @Mutation(() => Boolean) + @UseMiddleware(isAuth) + async changeCoverPhoto( + @Arg('key', () => String) key: string, + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + await User.update({ id: userId }, { coverPhoto: key }) + return true + } + + @Query(() => [PhotoResponse]) + @UseMiddleware(isAuth) + async avatars( + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `users/${userId}/photos/avatars` }).promise() + let keys = result?.Contents?.map(item => item.Key as string) + if (keys == undefined || keys.length == 0) { + return [] + } + else { + keys = keys.filter(k => k != `users/${userId}/photos/avatars`) + return Promise.all(keys.map(async key => { + return { + key, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } as PhotoResponse + })) + } + } + + @Query(() => [PhotoResponse]) + @UseMiddleware(isAuth) + async coverPhotos( + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const result = await s3.listObjectsV2({ Bucket: process.env.S3_BUCKET, Prefix: `users/${userId}/photos/coverPhotos` }).promise() + let keys = result?.Contents?.map(item => item.Key as string) + if (keys == undefined || keys.length == 0) { + return [] + } + else { + keys = keys.filter(k => k != `users/${userId}/photos/coverPhotos`) + return Promise.all(keys.map(async key => { + return { + key, + url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: key }) + } as PhotoResponse + })) + } + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async followUser( + @Arg('id', () => Int) id: number, + @Ctx() { req }: Context + ) { + const { userId } = req.session + if (id == userId) { + throw new Error('You can\'t follow yourself!') + } + const follow = await UserFollow.findOneBy({ followingUserId: userId, followedUserId: id }) + if (!follow) { + const user = await User.findOneBy({ id }) + if (!user) { + return false + } + await UserFollow.create({ followingUserId: userId, followedUserId: id }).save() + } + return true + } + + @UseMiddleware(isAuth) + @Mutation(() => Boolean) + async unfollowUser( + @Arg('id', () => Int) id: number, + @Ctx() { req }: Context + ) { + const { userId } = req.session + + const follow = await UserFollow.findOneBy({ followingUserId: userId, followedUserId: id }) + if (follow) { + await follow.remove() + } + return true + } + + @Mutation(() => [String], { nullable: true }) + async setSkills( + @Arg('skills', () => [String]) skills: string[], + @Ctx() { req }: Context + ): Promise { + const { userId } = req.session + const user = await User.findOneBy({ id: userId }) + if (!user) { + return [] + } + user.skills = skills + await user.save() + return user.skills + } +} \ No newline at end of file diff --git a/src/type-orm.source.ts b/src/type-orm.source.ts new file mode 100644 index 0000000..50cd122 --- /dev/null +++ b/src/type-orm.source.ts @@ -0,0 +1,4 @@ +import { DataSource } from 'typeorm' +import { typeORMConfig } from '@configs' + +export default new DataSource(typeORMConfig) \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..65ec153 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,49 @@ +import { + createApplicationLoader, + createApplicationStatusLoader, + createCommentVoteLoader, + createFollowLoader, + createModStatusLoader, + createOwnerStatusLoader, + createPageLoader, + createSpaceLoader, + createSubscriptionLoader, + createTagLoader, + createVoteLoader, + createUserFollowLoader, + createUserLoader, + createApplicantsNoLoader, + createFollowerNumberLoader, + createSubscriberNumberLoader, + createUserFollowerNumberLoader, + createUserFollowingNumberLoader +} from '@loaders' +import { Request, Response } from 'express' +import session from 'express-session' +import Redis from 'ioredis' +import { DataSource } from 'typeorm' + +export type Context = { + orm: DataSource + req: Request & { session: session.Session & Partial & { userId?: number } } + res: Response + redis: Redis + userLoader: ReturnType + voteLoader: ReturnType + spaceLoader: ReturnType + commentVoteLoader: ReturnType + tagLoader: ReturnType + subscriptionLoader: ReturnType + modStatusLoader: ReturnType + pageLoader: ReturnType + followLoader: ReturnType + ownerStatusLoader: ReturnType + userFollowLoader: ReturnType + applicationStatusLoader: ReturnType + applicationLoader: ReturnType + applicantsNoLoader: ReturnType + followerNumberLoader: ReturnType + subscriberNumberLoader: ReturnType + userFollowerNumberLoader: ReturnType + userFollowingNumberLoader: ReturnType +} \ No newline at end of file diff --git a/src/utils/getExtensionFromFilename.ts b/src/utils/getExtensionFromFilename.ts new file mode 100644 index 0000000..2b814c3 --- /dev/null +++ b/src/utils/getExtensionFromFilename.ts @@ -0,0 +1,3 @@ +export const getExtensionFromFilename = (filename: string): string => { + return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2) +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..9daa91e --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,8 @@ +export * from './isValidEmail' +export * from './sendEmail' +export * from './sleep' +export * from './validateLogin' +export * from './validateRegister' +export * from './toPostgresTime' +export * from './saveAttributes' +export * from './getExtensionFromFilename' \ No newline at end of file diff --git a/src/utils/isValidEmail.ts b/src/utils/isValidEmail.ts new file mode 100644 index 0000000..e70a5cc --- /dev/null +++ b/src/utils/isValidEmail.ts @@ -0,0 +1,2 @@ +export const isValidEmail = (email: string): boolean => + /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email) diff --git a/src/utils/saveAttributes.ts b/src/utils/saveAttributes.ts new file mode 100644 index 0000000..d5a8cc2 --- /dev/null +++ b/src/utils/saveAttributes.ts @@ -0,0 +1,9 @@ +export const saveAttributes = (org: E & any, attrs: I) => { + const keys = Object.keys(attrs) + for (const key of keys) { + const value = attrs[key as keyof I] + if (typeof value != undefined && value != null) { + org[key as keyof I] = value + } + } +} \ No newline at end of file diff --git a/src/utils/sendEmail.ts b/src/utils/sendEmail.ts new file mode 100644 index 0000000..55fe95b --- /dev/null +++ b/src/utils/sendEmail.ts @@ -0,0 +1,24 @@ +import nodemailer from 'nodemailer' + +export async function sendEmail(to: string, html: string) { + // const transporter = nodemailer.createTransport({ + // host: process.env.SMTP_HOST, + // port: process.env.SMTP_PORT, + // secure: (new Boolean(process.env.SMTP_SECURE).valueOf()), + // auth: { + // user: process.env.SMTP_USER, + // pass: process.env.SMTP_PASS + // } + // } as any) + + // const info = await transporter.sendMail({ + // from: 'comrootz@gmail.com', + // to: to, + // subject: 'Change password', + // html + // }) + + // console.log('Message sent: %s', info.messageId) + // console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info)) + console.log('Message sent') +} \ No newline at end of file diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts new file mode 100644 index 0000000..9642335 --- /dev/null +++ b/src/utils/sleep.ts @@ -0,0 +1 @@ +export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) \ No newline at end of file diff --git a/src/utils/toPostgresTime.ts b/src/utils/toPostgresTime.ts new file mode 100644 index 0000000..7c7d2ad --- /dev/null +++ b/src/utils/toPostgresTime.ts @@ -0,0 +1,4 @@ +export const toPostgresTime = (cursor: string) => { + const date = (new Date(parseInt(cursor))) + return date.toLocaleDateString() + ' ' + date.toLocaleTimeString() +} \ No newline at end of file diff --git a/src/utils/validateLogin.ts b/src/utils/validateLogin.ts new file mode 100644 index 0000000..4805563 --- /dev/null +++ b/src/utils/validateLogin.ts @@ -0,0 +1,59 @@ +import { isValidEmail } from '.' +import { FieldError } from '../graphql-types' + +interface LoginValidation { + input: 'Email' | 'Username' + errors: FieldError[] | null +} + +export const validateLogin = (emailOrUsername: string, password: string): LoginValidation => { + const result: LoginValidation = { + input: 'Email', + errors: [] + } + if (emailOrUsername.includes('@')) { // email provided + if (!isValidEmail(emailOrUsername)) { + result.errors!.push({ + field: 'emailOrUsername', + message: 'Invalid email.' + }) + } + } + else { // username provided + result.input = 'Username' + if (emailOrUsername.length <= 2) { + result.errors!.push({ + field: 'emailOrUsername', + message: 'Username must be longer than 2 characters.' + }) + } + if (emailOrUsername.length > 32) { + result.errors!.push({ + field: 'emailOrUsername', + message: 'Username must not be longer than 32 characters.' + }) + } + if (!/^[A-Za-z0-9_-]*$/.test(emailOrUsername)) { + result.errors!.push({ + field: 'emailOrUsername', + message: 'Username must contain only letters, numbers, underscores and dashes.' + }) + } + } + if (password.length < 8) { + result.errors!.push({ + field: 'password', + message: 'Password must be at least 8 characters.' + }) + } + if (password.length > 128) { + result.errors!.push({ + field: 'password', + message: 'Password must not be longer than 128 characters.' + }) + } + return { + input: result.input, + errors: result.errors!.length > 0 ? result.errors : null + } +} \ No newline at end of file diff --git a/src/utils/validateRegister.ts b/src/utils/validateRegister.ts new file mode 100644 index 0000000..35dfd2c --- /dev/null +++ b/src/utils/validateRegister.ts @@ -0,0 +1,43 @@ +import { isValidEmail } from '.' +import { FieldError } from '../graphql-types' + +export const validateRegister = (email: string, username: string, password: string): FieldError[] | null => { + const errors: FieldError[] = [] + if (!isValidEmail(email)) { + errors.push({ + field: 'email', + message: 'Invalid email.' + }) + } + if (username.length <= 2) { + errors.push({ + field: 'username', + message: 'Username must be longer than 2 characters.' + }) + } + if (username.length > 32) { + errors.push({ + field: 'username', + message: 'Username must not be longer than 32 characters.' + }) + } + if (!/^[A-Za-z0-9_-]*$/.test(username)) { + errors.push({ + field: 'username', + message: 'Username must contain only letters, numbers, underscores and dashes.' + }) + } + if (password.length < 8) { + errors.push({ + field: 'password', + message: 'Password must be at least 8 characters.' + }) + } + if (password.length > 128) { + errors.push({ + field: 'password', + message: 'Password must not be longer than 128 characters.' + }) + } + return errors.length > 0 ? errors : null +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c794cc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,119 @@ +{ + "ts-node": { + "swc": true, + "require": [ + "reflect-metadata", + "tsconfig-paths/register", + "dotenv-safe/config" + ] + }, + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": [ + "ES2022", + "ESNext" + ], + "skipLibCheck": true, + "sourceMap": true, + "outDir": "./dist", + "moduleResolution": "node", + "removeComments": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "resolveJsonModule": true, + "baseUrl": ".", + "paths": { + "@entities": [ + "./src/entities" + ], + "@entities/*": [ + "./src/entities/*" + ], + "@collections": [ + "./src/collections" + ], + "@collections/*": [ + "./src/collections/*" + ], + "@configs": [ + "./src/configs" + ], + "@configs/*": [ + "./src/configs/*" + ], + "@graphql-types": [ + "./src/graphql-types" + ], + "@graphql-types/*": [ + "./src/graphql-types/*" + ], + "@resolvers": [ + "./src/resolvers" + ], + "@resolvers/*": [ + "./src/resolvers/*" + ], + "@loaders": [ + "./src/loaders" + ], + "@loaders/*": [ + "./src/loaders/*" + ], + "@middlewares": [ + "./src/middlewares" + ], + "@middlewares/*": [ + "./src/middlewares/*" + ], + "@migrations": [ + "./src/migrations" + ], + "@migrations/*": [ + "./src/migrations/*" + ], + "@utils": [ + "./src/utils" + ], + "@utils/*": [ + "./src/utils/*" + ], + "@constants": [ + "./src/constants" + ], + "@constants/*": [ + "./src/constants/*" + ], + "@types": [ + "./src/types" + ], + "@types/*": [ + "./src/types/*" + ], + "@src": [ + "./src" + ], + "@src/*": [ + "./src/*" + ] + } + }, + "exclude": [ + "node_modules" + ], + "include": [ + "env.d.ts", + "./src/**/*.tsx", + "./src/**/*.ts" + ] +} \ No newline at end of file