From 53498cb5f1a5546c1b5180fcc38fb252f70011b3 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 25 Sep 2025 04:47:06 +0000 Subject: [PATCH] Initial version --- .gitignore | 3 + 502.html | 187 + Dockerfile | 17 + Dockerfile.dev | 73 + LICENSE | 681 +++ README.MD | 170 + app-shell/.eslintrc.cjs | 26 + app-shell/.prettierrc | 11 + app-shell/.sequelizerc | 7 + app-shell/Dockerfile | 23 + app-shell/README.md | 13 + app-shell/package.json | 42 + app-shell/src/_schema.json | 5 + app-shell/src/config.js | 16 + app-shell/src/helpers.js | 23 + app-shell/src/index.js | 54 + .../src/middlewares/check-permissions.js | 17 + app-shell/src/middlewares/modify-path.js | 8 + app-shell/src/routes/executor.js | 312 ++ app-shell/src/routes/vcs.js | 40 + app-shell/src/services/database.js | 88 + app-shell/src/services/executor.js | 1206 +++++ .../notifications/errors/forbidden.js | 16 + .../notifications/errors/validation.js | 16 + .../src/services/notifications/helpers.js | 30 + app-shell/src/services/notifications/list.js | 100 + app-shell/src/services/project-events.js | 67 + app-shell/src/services/vcs.js | 1205 +++++ app-shell/yarn.lock | 3044 +++++++++++ backend/.sequelizerc | 7 + backend/Dockerfile | 23 + backend/README.md | 56 + backend/package.json | 53 + backend/src/auth/auth.js | 27 + backend/src/config.js | 59 + backend/src/db/api/users.js | 692 +++ backend/src/db/db.config.js | 33 + backend/src/db/migrations/1758775130656.js | 268 + backend/src/db/models/index.js | 38 + backend/src/db/models/users.js | 148 + backend/src/db/reset.js | 16 + .../db/seeders/20200430130759-admin-user.js | 66 + backend/src/db/utils.js | 27 + backend/src/helpers.js | 23 + backend/src/index.js | 107 + backend/src/middlewares/upload.js | 11 + backend/src/routes/auth.js | 200 + backend/src/routes/contactForm.js | 22 + backend/src/routes/file.js | 32 + backend/src/routes/openai.js | 251 + backend/src/routes/pexels.js | 104 + backend/src/routes/search.js | 47 + backend/src/routes/users.js | 421 ++ backend/src/services/auth.js | 257 + .../notifications/errors/forbidden.js | 17 + .../notifications/errors/validation.js | 18 + backend/src/services/notifications/helpers.js | 35 + backend/src/services/notifications/list.js | 104 + backend/src/services/openai.js | 51 + backend/src/services/search.js | 127 + backend/src/services/users.js | 163 + backend/watcher.js | 45 + backend/yarn.lock | 4470 +++++++++++++++++ cloudbuild.yaml | 14 + community-template.png | Bin 0 -> 87219 bytes docker/.gitignore | 1 + docker/README.md | 46 + docker/docker-compose.yml | 35 + docker/start-backend.sh | 2 + docker/wait-for-it.sh | 182 + frontend/Dockerfile | 19 + frontend/LICENSE-justboil | 21 + frontend/README.md | 89 + frontend/next-env.d.ts | 6 + frontend/next.config.mjs | 28 + frontend/package.json | 67 + frontend/postcss.config.js | 9 + frontend/prettier.config.js | 13 + frontend/public/data-sources/clients.json | 1 + frontend/public/data-sources/history.json | 1 + frontend/public/favicon.svg | 27 + frontend/src/colors.ts | 138 + frontend/src/components/AsideMenu.tsx | 30 + frontend/src/components/AsideMenuItem.tsx | 102 + frontend/src/components/AsideMenuLayer.tsx | 58 + frontend/src/components/AsideMenuList.tsx | 31 + frontend/src/components/BaseButton.tsx | 96 + frontend/src/components/BaseButtons.tsx | 38 + frontend/src/components/BaseDivider.tsx | 14 + frontend/src/components/BaseIcon.tsx | 32 + frontend/src/components/CardBox.tsx | 64 + .../src/components/CardBoxComponentBody.tsx | 12 + .../src/components/CardBoxComponentEmpty.tsx | 11 + .../src/components/CardBoxComponentFooter.tsx | 10 + .../src/components/CardBoxComponentTitle.tsx | 17 + frontend/src/components/CardBoxModal.tsx | 59 + .../src/components/ChartLineSample/config.ts | 54 + .../src/components/ChartLineSample/index.tsx | 37 + frontend/src/components/ClickOutside.tsx | 35 + .../src/components/DataGridMultiSelect.tsx | 55 + frontend/src/components/DevModeBadge.tsx | 150 + .../src/components/DragDropFilePicker.tsx | 124 + frontend/src/components/ErrorBoundary.tsx | 218 + frontend/src/components/FooterBar.tsx | 35 + frontend/src/components/FormCheckRadio.tsx | 20 + .../src/components/FormCheckRadioGroup.tsx | 20 + frontend/src/components/FormField.tsx | 80 + frontend/src/components/FormFilePicker.tsx | 92 + frontend/src/components/FormImagePicker.tsx | 90 + frontend/src/components/IconRounded.tsx | 34 + .../src/components/ListActionsPopover.tsx | 114 + frontend/src/components/LoadingSpinner.tsx | 18 + frontend/src/components/Logo/index.tsx | 15 + frontend/src/components/NavBar.tsx | 57 + frontend/src/components/NavBarItem.tsx | 132 + frontend/src/components/NavBarItemPlain.tsx | 30 + frontend/src/components/NavBarMenuList.tsx | 19 + frontend/src/components/NotificationBar.tsx | 57 + frontend/src/components/OverlayLayer.tsx | 41 + frontend/src/components/Pagination.tsx | 85 + .../src/components/PasswordSetOrReset.tsx | 113 + frontend/src/components/RichTextField.tsx | 42 + frontend/src/components/Search.tsx | 50 + frontend/src/components/SearchResults.tsx | 83 + frontend/src/components/SectionFullScreen.tsx | 27 + frontend/src/components/SectionMain.tsx | 10 + frontend/src/components/SectionTitle.tsx | 27 + .../components/SectionTitleLineWithButton.tsx | 29 + frontend/src/components/SelectField.tsx | 53 + frontend/src/components/SwitchField.tsx | 19 + .../src/components/TableSampleClients.tsx | 141 + .../src/components/Uploaders/FilesUploader.js | 150 + .../components/Uploaders/ImagesUploader.js | 237 + .../src/components/Uploaders/UploadService.js | 79 + frontend/src/components/UserAvatar.tsx | 46 + .../src/components/UserAvatarCurrentUser.tsx | 43 + frontend/src/components/UserCard.tsx | 48 + frontend/src/components/Users/CardUsers.tsx | 138 + frontend/src/components/Users/ListUsers.tsx | 97 + frontend/src/components/Users/TableUsers.tsx | 441 ++ .../components/Users/configureUsersCols.tsx | 131 + frontend/src/config.ts | 15 + frontend/src/css/_app.css | 32 + frontend/src/css/_calendar.css | 37 + frontend/src/css/_checkbox-radio-switch.css | 59 + frontend/src/css/_helper.css | 23 + frontend/src/css/_progress.css | 21 + frontend/src/css/_scrollbars.css | 41 + frontend/src/css/_select-dropdown.css | 31 + frontend/src/css/_table.css | 115 + frontend/src/css/_theme.css | 103 + frontend/src/css/main.css | 33 + frontend/src/css/tailwind/_base.css | 1 + frontend/src/css/tailwind/_components.css | 1 + frontend/src/css/tailwind/_utilities.css | 1 + frontend/src/helpers/dataFormatter.js | 42 + frontend/src/helpers/fileSaver.ts | 6 + frontend/src/helpers/humanize.ts | 11 + frontend/src/helpers/notifyStateHandler.ts | 31 + frontend/src/hooks/sampleData.ts | 22 + frontend/src/hooks/useDevCompilationStatus.ts | 44 + frontend/src/interfaces/index.ts | 109 + frontend/src/layouts/Authenticated.tsx | 111 + frontend/src/layouts/Guest.tsx | 17 + frontend/src/menuAside.ts | 34 + frontend/src/menuNavBar.ts | 49 + frontend/src/pages/_app.tsx | 122 + frontend/src/pages/api/hello.js | 5 + frontend/src/pages/api/logError.ts | 83 + frontend/src/pages/dashboard.tsx | 100 + frontend/src/pages/error.tsx | 35 + frontend/src/pages/forgot.tsx | 82 + frontend/src/pages/forms.tsx | 142 + frontend/src/pages/index.tsx | 66 + frontend/src/pages/login.tsx | 198 + frontend/src/pages/password-reset.tsx | 12 + frontend/src/pages/privacy-policy.tsx | 292 ++ frontend/src/pages/profile.tsx | 175 + frontend/src/pages/register.tsx | 89 + frontend/src/pages/search.tsx | 86 + frontend/src/pages/tables.tsx | 33 + frontend/src/pages/terms-of-use.tsx | 206 + frontend/src/pages/users/[usersId].tsx | 172 + frontend/src/pages/users/users-edit.tsx | 170 + frontend/src/pages/users/users-list.tsx | 128 + frontend/src/pages/users/users-new.tsx | 132 + frontend/src/pages/users/users-table.tsx | 129 + frontend/src/pages/users/users-view.tsx | 101 + frontend/src/pages/verify-email.tsx | 62 + frontend/src/stores/authSlice.ts | 124 + frontend/src/stores/hooks.ts | 6 + frontend/src/stores/mainSlice.ts | 36 + frontend/src/stores/store.ts | 21 + frontend/src/stores/styleSlice.ts | 101 + frontend/src/stores/users/usersSlice.ts | 229 + frontend/src/stores/usersSlice.ts | 117 + frontend/src/styles.ts | 103 + frontend/tailwind.config.js | 108 + frontend/tsconfig.json | 30 + frontend/yarn.lock | 3608 +++++++++++++ nginx.conf | 89 + package.json | 9 + pids/backend.pid | 1 + pids/frontend.pid | 1 + 204 files changed, 28599 insertions(+) create mode 100644 .gitignore create mode 100644 502.html create mode 100644 Dockerfile create mode 100644 Dockerfile.dev create mode 100644 LICENSE create mode 100644 README.MD create mode 100644 app-shell/.eslintrc.cjs create mode 100644 app-shell/.prettierrc create mode 100644 app-shell/.sequelizerc create mode 100644 app-shell/Dockerfile create mode 100644 app-shell/README.md create mode 100644 app-shell/package.json create mode 100644 app-shell/src/_schema.json create mode 100644 app-shell/src/config.js create mode 100644 app-shell/src/helpers.js create mode 100644 app-shell/src/index.js create mode 100644 app-shell/src/middlewares/check-permissions.js create mode 100644 app-shell/src/middlewares/modify-path.js create mode 100644 app-shell/src/routes/executor.js create mode 100644 app-shell/src/routes/vcs.js create mode 100644 app-shell/src/services/database.js create mode 100644 app-shell/src/services/executor.js create mode 100644 app-shell/src/services/notifications/errors/forbidden.js create mode 100644 app-shell/src/services/notifications/errors/validation.js create mode 100644 app-shell/src/services/notifications/helpers.js create mode 100644 app-shell/src/services/notifications/list.js create mode 100644 app-shell/src/services/project-events.js create mode 100644 app-shell/src/services/vcs.js create mode 100644 app-shell/yarn.lock create mode 100644 backend/.sequelizerc create mode 100644 backend/Dockerfile create mode 100644 backend/README.md create mode 100644 backend/package.json create mode 100644 backend/src/auth/auth.js create mode 100644 backend/src/config.js create mode 100644 backend/src/db/api/users.js create mode 100644 backend/src/db/db.config.js create mode 100644 backend/src/db/migrations/1758775130656.js create mode 100644 backend/src/db/models/index.js create mode 100644 backend/src/db/models/users.js create mode 100644 backend/src/db/reset.js create mode 100644 backend/src/db/seeders/20200430130759-admin-user.js create mode 100644 backend/src/db/utils.js create mode 100644 backend/src/helpers.js create mode 100644 backend/src/index.js create mode 100644 backend/src/middlewares/upload.js create mode 100644 backend/src/routes/auth.js create mode 100644 backend/src/routes/contactForm.js create mode 100644 backend/src/routes/file.js create mode 100644 backend/src/routes/openai.js create mode 100644 backend/src/routes/pexels.js create mode 100644 backend/src/routes/search.js create mode 100644 backend/src/routes/users.js create mode 100644 backend/src/services/auth.js create mode 100644 backend/src/services/notifications/errors/forbidden.js create mode 100644 backend/src/services/notifications/errors/validation.js create mode 100644 backend/src/services/notifications/helpers.js create mode 100644 backend/src/services/notifications/list.js create mode 100644 backend/src/services/openai.js create mode 100644 backend/src/services/search.js create mode 100644 backend/src/services/users.js create mode 100644 backend/watcher.js create mode 100644 backend/yarn.lock create mode 100644 cloudbuild.yaml create mode 100644 community-template.png create mode 100644 docker/.gitignore create mode 100644 docker/README.md create mode 100644 docker/docker-compose.yml create mode 100644 docker/start-backend.sh create mode 100644 docker/wait-for-it.sh create mode 100644 frontend/Dockerfile create mode 100644 frontend/LICENSE-justboil create mode 100644 frontend/README.md create mode 100644 frontend/next-env.d.ts create mode 100644 frontend/next.config.mjs create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/prettier.config.js create mode 100644 frontend/public/data-sources/clients.json create mode 100644 frontend/public/data-sources/history.json create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/src/colors.ts create mode 100644 frontend/src/components/AsideMenu.tsx create mode 100644 frontend/src/components/AsideMenuItem.tsx create mode 100644 frontend/src/components/AsideMenuLayer.tsx create mode 100644 frontend/src/components/AsideMenuList.tsx create mode 100644 frontend/src/components/BaseButton.tsx create mode 100644 frontend/src/components/BaseButtons.tsx create mode 100644 frontend/src/components/BaseDivider.tsx create mode 100644 frontend/src/components/BaseIcon.tsx create mode 100644 frontend/src/components/CardBox.tsx create mode 100644 frontend/src/components/CardBoxComponentBody.tsx create mode 100644 frontend/src/components/CardBoxComponentEmpty.tsx create mode 100644 frontend/src/components/CardBoxComponentFooter.tsx create mode 100644 frontend/src/components/CardBoxComponentTitle.tsx create mode 100644 frontend/src/components/CardBoxModal.tsx create mode 100644 frontend/src/components/ChartLineSample/config.ts create mode 100644 frontend/src/components/ChartLineSample/index.tsx create mode 100644 frontend/src/components/ClickOutside.tsx create mode 100644 frontend/src/components/DataGridMultiSelect.tsx create mode 100644 frontend/src/components/DevModeBadge.tsx create mode 100644 frontend/src/components/DragDropFilePicker.tsx create mode 100644 frontend/src/components/ErrorBoundary.tsx create mode 100644 frontend/src/components/FooterBar.tsx create mode 100644 frontend/src/components/FormCheckRadio.tsx create mode 100644 frontend/src/components/FormCheckRadioGroup.tsx create mode 100644 frontend/src/components/FormField.tsx create mode 100644 frontend/src/components/FormFilePicker.tsx create mode 100644 frontend/src/components/FormImagePicker.tsx create mode 100644 frontend/src/components/IconRounded.tsx create mode 100644 frontend/src/components/ListActionsPopover.tsx create mode 100644 frontend/src/components/LoadingSpinner.tsx create mode 100644 frontend/src/components/Logo/index.tsx create mode 100644 frontend/src/components/NavBar.tsx create mode 100644 frontend/src/components/NavBarItem.tsx create mode 100644 frontend/src/components/NavBarItemPlain.tsx create mode 100644 frontend/src/components/NavBarMenuList.tsx create mode 100644 frontend/src/components/NotificationBar.tsx create mode 100644 frontend/src/components/OverlayLayer.tsx create mode 100644 frontend/src/components/Pagination.tsx create mode 100644 frontend/src/components/PasswordSetOrReset.tsx create mode 100644 frontend/src/components/RichTextField.tsx create mode 100644 frontend/src/components/Search.tsx create mode 100644 frontend/src/components/SearchResults.tsx create mode 100644 frontend/src/components/SectionFullScreen.tsx create mode 100644 frontend/src/components/SectionMain.tsx create mode 100644 frontend/src/components/SectionTitle.tsx create mode 100644 frontend/src/components/SectionTitleLineWithButton.tsx create mode 100644 frontend/src/components/SelectField.tsx create mode 100644 frontend/src/components/SwitchField.tsx create mode 100644 frontend/src/components/TableSampleClients.tsx create mode 100644 frontend/src/components/Uploaders/FilesUploader.js create mode 100644 frontend/src/components/Uploaders/ImagesUploader.js create mode 100644 frontend/src/components/Uploaders/UploadService.js create mode 100644 frontend/src/components/UserAvatar.tsx create mode 100644 frontend/src/components/UserAvatarCurrentUser.tsx create mode 100644 frontend/src/components/UserCard.tsx create mode 100644 frontend/src/components/Users/CardUsers.tsx create mode 100644 frontend/src/components/Users/ListUsers.tsx create mode 100644 frontend/src/components/Users/TableUsers.tsx create mode 100644 frontend/src/components/Users/configureUsersCols.tsx create mode 100644 frontend/src/config.ts create mode 100644 frontend/src/css/_app.css create mode 100644 frontend/src/css/_calendar.css create mode 100644 frontend/src/css/_checkbox-radio-switch.css create mode 100644 frontend/src/css/_helper.css create mode 100644 frontend/src/css/_progress.css create mode 100644 frontend/src/css/_scrollbars.css create mode 100644 frontend/src/css/_select-dropdown.css create mode 100644 frontend/src/css/_table.css create mode 100644 frontend/src/css/_theme.css create mode 100644 frontend/src/css/main.css create mode 100644 frontend/src/css/tailwind/_base.css create mode 100644 frontend/src/css/tailwind/_components.css create mode 100644 frontend/src/css/tailwind/_utilities.css create mode 100644 frontend/src/helpers/dataFormatter.js create mode 100644 frontend/src/helpers/fileSaver.ts create mode 100644 frontend/src/helpers/humanize.ts create mode 100644 frontend/src/helpers/notifyStateHandler.ts create mode 100644 frontend/src/hooks/sampleData.ts create mode 100644 frontend/src/hooks/useDevCompilationStatus.ts create mode 100644 frontend/src/interfaces/index.ts create mode 100644 frontend/src/layouts/Authenticated.tsx create mode 100644 frontend/src/layouts/Guest.tsx create mode 100644 frontend/src/menuAside.ts create mode 100644 frontend/src/menuNavBar.ts create mode 100644 frontend/src/pages/_app.tsx create mode 100644 frontend/src/pages/api/hello.js create mode 100644 frontend/src/pages/api/logError.ts create mode 100644 frontend/src/pages/dashboard.tsx create mode 100644 frontend/src/pages/error.tsx create mode 100644 frontend/src/pages/forgot.tsx create mode 100644 frontend/src/pages/forms.tsx create mode 100644 frontend/src/pages/index.tsx create mode 100644 frontend/src/pages/login.tsx create mode 100644 frontend/src/pages/password-reset.tsx create mode 100644 frontend/src/pages/privacy-policy.tsx create mode 100644 frontend/src/pages/profile.tsx create mode 100644 frontend/src/pages/register.tsx create mode 100644 frontend/src/pages/search.tsx create mode 100644 frontend/src/pages/tables.tsx create mode 100644 frontend/src/pages/terms-of-use.tsx create mode 100644 frontend/src/pages/users/[usersId].tsx create mode 100644 frontend/src/pages/users/users-edit.tsx create mode 100644 frontend/src/pages/users/users-list.tsx create mode 100644 frontend/src/pages/users/users-new.tsx create mode 100644 frontend/src/pages/users/users-table.tsx create mode 100644 frontend/src/pages/users/users-view.tsx create mode 100644 frontend/src/pages/verify-email.tsx create mode 100644 frontend/src/stores/authSlice.ts create mode 100644 frontend/src/stores/hooks.ts create mode 100644 frontend/src/stores/mainSlice.ts create mode 100644 frontend/src/stores/store.ts create mode 100644 frontend/src/stores/styleSlice.ts create mode 100644 frontend/src/stores/users/usersSlice.ts create mode 100644 frontend/src/stores/usersSlice.ts create mode 100644 frontend/src/styles.ts create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/yarn.lock create mode 100644 nginx.conf create mode 100644 package.json create mode 100644 pids/backend.pid create mode 100644 pids/frontend.pid diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e427ff3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +*/node_modules/ +*/build/ diff --git a/502.html b/502.html new file mode 100644 index 0000000..688890d --- /dev/null +++ b/502.html @@ -0,0 +1,187 @@ + + + + + + + Service Starting + + + + +
+

Loading the app, just a moment…

+

The application is currently launching. The page will automatically refresh once site is + available.

+
+

betterp

+

Comprehensive ERP software with extensive modules.

+
+
+ +
+
+
+ +
+
+ + + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..affcee8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM node:20.15.1-alpine AS builder +RUN apk add --no-cache git +WORKDIR /app +COPY frontend/package.json frontend/yarn.lock ./ +RUN yarn install --pure-lockfile +COPY frontend . +RUN yarn build + +FROM node:20.15.1-alpine +WORKDIR /app +COPY backend/package.json backend/yarn.lock ./ +RUN yarn install --pure-lockfile +COPY backend . + +COPY --from=builder /app/build /app/public +CMD ["yarn", "start"] + diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..aa3b625 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,73 @@ +# Base image for Node.js dependencies +FROM node:20.15.1-alpine AS frontend-deps +RUN apk add --no-cache git +WORKDIR /app/frontend +COPY frontend/package.json frontend/yarn.lock ./ +RUN yarn install --pure-lockfile + +FROM node:20.15.1-alpine AS backend-deps +RUN apk add --no-cache git +WORKDIR /app/backend +COPY backend/package.json backend/yarn.lock ./ +RUN yarn install --pure-lockfile + +FROM node:20.15.1-alpine AS app-shell-deps +RUN apk add --no-cache git +WORKDIR /app/app-shell +COPY app-shell/package.json app-shell/yarn.lock ./ +RUN yarn install --pure-lockfile + +# Nginx setup and application build +FROM node:20.15.1-alpine AS build +RUN apk add --no-cache git nginx +RUN apk add --no-cache lsof procps +RUN yarn global add concurrently + +RUN mkdir -p /app/pids + +# Make sure to add yarn global bin to PATH +ENV PATH /root/.yarn/bin:/root/.config/yarn/global/node_modules/.bin:$PATH + +# Copy dependencies +WORKDIR /app +COPY --from=frontend-deps /app/frontend /app/frontend +COPY --from=backend-deps /app/backend /app/backend +COPY --from=app-shell-deps /app/app-shell /app/app-shell + +COPY frontend /app/frontend +COPY backend /app/backend +COPY app-shell /app/app-shell +COPY docker /app/docker + +# Copy Nginx configuration +COPY nginx.conf /etc/nginx/nginx.conf + +# Copy custom error page +COPY 502.html /usr/share/nginx/html/502.html + +# Change owner and permissions of the error page +RUN chown nginx:nginx /usr/share/nginx/html/502.html && \ + chmod 644 /usr/share/nginx/html/502.html + +# Copy all files from root to /app +COPY . /app + +# Expose the port the app runs on +EXPOSE 8080 +ENV NODE_ENV=dev_stage +ENV FRONT_PORT=3001 +ENV BACKEND_PORT=3000 +ENV APP_SHELL_PORT=4000 + +CMD ["sh", "-c", "\ + yarn --cwd /app/frontend dev & echo $! > /app/pids/frontend.pid && \ + yarn --cwd /app/backend start & echo $! > /app/pids/backend.pid && \ + sleep 10 && nginx -g 'daemon off;' & \ + NGINX_PID=$! && \ + echo 'Waiting for backend (port 3000) to be available...' && \ + while ! nc -z localhost ${BACKEND_PORT}; do \ + sleep 2; \ + done && \ + echo 'Backend is up. Starting app_shell for Git check...' && \ + yarn --cwd /app/app-shell start && \ + wait $NGINX_PID"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f1083d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,681 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +Flatlogic Template Exception +-------------------------------- +As a special exception, you may use this template pack to generate source +code and distribute or license that generated code under terms of your +choice, provided that the generated code does not contain a substantial +portion of the template pack itself. diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..8655a0d --- /dev/null +++ b/README.MD @@ -0,0 +1,170 @@ +# Project Setup & Local Development + +## Tech Stack + +| Layer | Technology | +| --------- | -------------------- | +| Front‑end | **React JS** | +| Back‑end | **Node JS** | +| Database | **PostgreSQL** | +| Container | **Docker & Compose** | + +--- + +## 1 — Run Locally (without Docker) + +### 1.1 Backend + +```bash +cd backend +# install dependencies +yarn install +``` + +\#### Configure PostgreSQL + +
+macOS + +```bash +brew install postgres +``` + +
+
+Ubuntu + +```bash +sudo apt update +sudo apt install postgresql postgresql-contrib +``` + +
+ +\##### Create DB & Admin User + +```bash +# log in as default super‑user +psql postgres -U postgres + +-- inside psql +CREATE ROLE admin WITH LOGIN PASSWORD 'admin_pass'; +ALTER ROLE admin CREATEDB; +\q + +# log in as the new user +psql postgres -U admin + +-- inside psql +CREATE DATABASE db_; +GRANT ALL PRIVILEGES ON DATABASE db_ TO admin; +\q +``` + +\##### Migrate & Start + +```bash +yarn db:create # generate schema +yarn start # production build +``` + +### 1.2 Frontend + +```bash +cd frontend +yarn install +yarn start +``` + +> Front‑end dev‑server runs at **[http://localhost:3000](http://localhost:3000)** by default. + +--- + +## 2 — Run with Docker + +```bash +cd docker +chmod +x wait-for-it.sh start-backend.sh +``` + +| Scenario | Command | +| ------------------------- | ---------------------------------- | +| **Fresh DB volume** | `rm -rf data && docker-compose up` | +| **Reuse existing volume** | `docker-compose up` | + +Then open **[http://localhost:3000](http://localhost:3000)**. + +Stop services with **Ctrl + C** or: + +```bash +docker-compose down +``` + +> **Heads‑up:** Files inside the `docker/` folder and the root `Dockerfile` are used for cloud deployment. Changing them may break the pipeline. + +--- + +## Folder Structure (top‑level) + +```text +├── backend/ # Node JS API & services +├── frontend/ # React application +├── docker/ # Compose files & helper scripts +└── README.md # (this file) +``` + +--- + +## Troubleshooting + +### “connection refused” + +1. Port closed or backlog full. +2. Firewall (local or network). +3. Service not running. + +Verify with: + +```bash +telnet +``` + +### macOS + +```bash +sudo service ssh status +``` + +### Ubuntu – IP conflict check + +```bash +arp-scan -I eth0 -l | grep +arping +``` + +### Reset PostgreSQL schema (macOS example) + +```sql +DROP SCHEMA public CASCADE; +CREATE SCHEMA public; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO public; +``` + +--- + +## Cheat‑sheet + +| Task | Command | +| ----------------------- | ---------------------------------- | +| **Create DB schema** | `yarn db:create` | +| **Start backend** | `yarn start` | +| **Start dev front‑end** | `cd frontend && yarn start` | +| **Compose up (fresh)** | `rm -rf data && docker-compose up` | +| **Compose down** | `docker-compose down` | + +
+ +--- + +Made with ❤️ by Flatlogic Platform. diff --git a/app-shell/.eslintrc.cjs b/app-shell/.eslintrc.cjs new file mode 100644 index 0000000..563d159 --- /dev/null +++ b/app-shell/.eslintrc.cjs @@ -0,0 +1,26 @@ +const globals = require('globals'); + +module.exports = [ + { + files: ['**/*.js', '**/*.ts', '**/*.tsx'], + languageOptions: { + ecmaVersion: 2021, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + }, + parser: '@typescript-eslint/parser', + }, + plugins: ['@typescript-eslint'], + rules: { + 'no-unused-vars': 'warn', + 'no-console': 'off', + 'indent': ['error', 2], + 'quotes': ['error', 'single'], + 'semi': ['error', 'always'], + + '@typescript-eslint/no-unused-vars': 'warn', + }, + }, +]; \ No newline at end of file diff --git a/app-shell/.prettierrc b/app-shell/.prettierrc new file mode 100644 index 0000000..bb087f2 --- /dev/null +++ b/app-shell/.prettierrc @@ -0,0 +1,11 @@ +{ + "singleQuote": true, + "tabWidth": 2, + "printWidth": 80, + "trailingComma": "all", + "quoteProps": "as-needed", + "jsxSingleQuote": true, + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always" +} diff --git a/app-shell/.sequelizerc b/app-shell/.sequelizerc new file mode 100644 index 0000000..fe89188 --- /dev/null +++ b/app-shell/.sequelizerc @@ -0,0 +1,7 @@ +const path = require('path'); +module.exports = { + "config": path.resolve("src", "db", "db.config.js"), + "models-path": path.resolve("src", "db", "models"), + "seeders-path": path.resolve("src", "db", "seeders"), + "migrations-path": path.resolve("src", "db", "migrations") +}; \ No newline at end of file diff --git a/app-shell/Dockerfile b/app-shell/Dockerfile new file mode 100644 index 0000000..eb79c5d --- /dev/null +++ b/app-shell/Dockerfile @@ -0,0 +1,23 @@ +FROM node:20.15.1-alpine + +RUN apk update && apk add bash +# Create app directory +WORKDIR /usr/src/app + +# Install app dependencies +# A wildcard is used to ensure both package.json AND package-lock.json are copied +# where available (npm@5+) +COPY package*.json ./ + +RUN yarn install +# If you are building your code for production +# RUN npm ci --only=production + + +# Bundle app source +COPY . . + + +EXPOSE 4000 + +CMD [ "yarn", "start" ] diff --git a/app-shell/README.md b/app-shell/README.md new file mode 100644 index 0000000..c53191f --- /dev/null +++ b/app-shell/README.md @@ -0,0 +1,13 @@ +#test - template backend, + +#### Run App on local machine: + +##### Install local dependencies: + +- `yarn install` + +--- + +##### Start build: + +- `yarn start` diff --git a/app-shell/package.json b/app-shell/package.json new file mode 100644 index 0000000..e33f634 --- /dev/null +++ b/app-shell/package.json @@ -0,0 +1,42 @@ +{ + "name": "app-shell", + "description": "app-shell", + "scripts": { + "start": "node ./src/index.js" + }, + "dependencies": { + "@babel/parser": "^7.26.7", + "adm-zip": "^0.5.16", + "axios": "^1.6.7", + "bcrypt": "5.1.1", + "cors": "2.8.5", + "eslint": "^9.13.0", + "express": "4.18.2", + "formidable": "1.2.2", + "helmet": "4.1.1", + "json2csv": "^5.0.7", + "jsonwebtoken": "8.5.1", + "lodash": "4.17.21", + "moment": "2.30.1", + "multer": "^1.4.4", + "passport": "^0.7.0", + "passport-google-oauth2": "^0.2.0", + "passport-jwt": "^4.0.1", + "passport-microsoft": "^0.1.0", + "postcss": "^8.5.1", + "sequelize-json-schema": "^2.1.1", + "pg": "^8.13.3" + }, + "engines": { + "node": ">=18" + }, + "private": true, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^8.12.2", + "@typescript-eslint/parser": "^8.12.2", + "cross-env": "7.0.3", + "mocha": "8.1.3", + "nodemon": "^3.1.7", + "sequelize-cli": "6.6.2" + } +} diff --git a/app-shell/src/_schema.json b/app-shell/src/_schema.json new file mode 100644 index 0000000..2bc258b --- /dev/null +++ b/app-shell/src/_schema.json @@ -0,0 +1,5 @@ + + +{ + "Initial version": "{\"iv\":\"lks/s2KsoSr9GkKQ\",\"encryptedData\":\"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\"}" +} diff --git a/app-shell/src/config.js b/app-shell/src/config.js new file mode 100644 index 0000000..094db64 --- /dev/null +++ b/app-shell/src/config.js @@ -0,0 +1,16 @@ + + +const config = { + schema_encryption_key: process.env.SCHEMA_ENCRYPTION_KEY || '', + + project_uuid: '3b603a71-a1c3-41f0-8f99-9539bcd4f7bb', + flHost: process.env.NODE_ENV === 'production' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects', + + gitea_domain: process.env.GITEA_DOMAIN || 'gitea.flatlogic.app', + gitea_username: process.env.GITEA_USERNAME || 'admin', + gitea_api_token: process.env.GITEA_API_TOKEN || null, + github_repo_url: process.env.GITHUB_REPO_URL || null, + github_token: process.env.GITHUB_TOKEN || null, +}; + +module.exports = config; diff --git a/app-shell/src/helpers.js b/app-shell/src/helpers.js new file mode 100644 index 0000000..1d918b5 --- /dev/null +++ b/app-shell/src/helpers.js @@ -0,0 +1,23 @@ +const jwt = require('jsonwebtoken'); +const config = require('./config'); + +module.exports = class Helpers { + static wrapAsync(fn) { + return function (req, res, next) { + fn(req, res, next).catch(next); + }; + } + + static commonErrorHandler(error, req, res, next) { + if ([400, 403, 404].includes(error.code)) { + return res.status(error.code).send(error.message); + } + + console.error(error); + return res.status(500).send(error.message); + } + + static jwtSign(data) { + return jwt.sign(data, config.secret_key, { expiresIn: '6h' }); + } +}; diff --git a/app-shell/src/index.js b/app-shell/src/index.js new file mode 100644 index 0000000..cc74c75 --- /dev/null +++ b/app-shell/src/index.js @@ -0,0 +1,54 @@ +const express = require('express'); +const cors = require('cors'); +const app = express(); +const bodyParser = require('body-parser'); +const checkPermissions = require('./middlewares/check-permissions'); +const modifyPath = require('./middlewares/modify-path'); +const VCS = require('./services/vcs'); + +const executorRoutes = require('./routes/executor'); +const vcsRoutes = require('./routes/vcs'); + +// Function to initialize the Git repository +function initRepo() { + const projectId = '34374'; + return VCS.initRepo(projectId); +} + +// Start the Express app on APP_SHELL_PORT (4000) +function startServer() { + const PORT = 4000; + app.listen(PORT, () => { + console.log(`Listening on port ${PORT}`); + }); +} + +// Run Git check after the server is up +function runGitCheck() { + initRepo() + .then(result => { + console.log(result?.message ? result.message : result); + // Here you can add additional logic if needed + }) + .catch(err => { + console.error('Error during repo initialization:', err); + // Optionally exit the process if Git check is critical: + // process.exit(1); + }); +} + +app.use(cors({ origin: true })); +app.use(bodyParser.json()); +app.use(checkPermissions); +app.use(modifyPath); + +app.use('/executor', executorRoutes); +app.use('/vcs', vcsRoutes); + +// Start the app_shell server +startServer(); + +// Now perform Git check +runGitCheck(); + +module.exports = app; diff --git a/app-shell/src/middlewares/check-permissions.js b/app-shell/src/middlewares/check-permissions.js new file mode 100644 index 0000000..cc9d90a --- /dev/null +++ b/app-shell/src/middlewares/check-permissions.js @@ -0,0 +1,17 @@ +const config = require('../config'); + +function checkPermissions(req, res, next) { + const project_uuid = config.project_uuid; + const requiredHeader = 'X-Project-UUID'; + const headerValue = req.headers[requiredHeader.toLowerCase()]; + // Logging whatever request we're getting + console.log('Request:', req.url, req.method, req.body, req.headers); + + if (headerValue && headerValue === project_uuid) { + next(); + } else { + res.status(403).send({ error: 'Stop right there, criminal scum! Your project UUID is invalid or missing.' }); + } +} + +module.exports = checkPermissions; \ No newline at end of file diff --git a/app-shell/src/middlewares/modify-path.js b/app-shell/src/middlewares/modify-path.js new file mode 100644 index 0000000..0154280 --- /dev/null +++ b/app-shell/src/middlewares/modify-path.js @@ -0,0 +1,8 @@ +function modifyPath(req, res, next) { + if (req.body && req.body.path) { + req.body.path = '../../../' + req.body.path; + } + next(); + } + +module.exports = modifyPath; \ No newline at end of file diff --git a/app-shell/src/routes/executor.js b/app-shell/src/routes/executor.js new file mode 100644 index 0000000..588cfff --- /dev/null +++ b/app-shell/src/routes/executor.js @@ -0,0 +1,312 @@ +const express = require('express'); +const multer = require('multer'); +const upload = multer({ dest: 'uploads/' }); +const fs = require('fs'); + +const ExecutorService = require('../services/executor'); + +const wrapAsync = require('../helpers').wrapAsync; + +const router = express.Router(); + +router.post( + '/read_project_tree', + wrapAsync(async (req, res) => { + const { path } = req.body; + const tree = await ExecutorService.readProjectTree(path); + res.status(200).send(tree); + }), +); + +router.post( + '/read_file', + wrapAsync(async (req, res) => { + const { path, showLines } = req.body; + const content = await ExecutorService.readFileContents(path, showLines); + res.status(200).send(content); + }), +); + +router.post( + '/count_file_lines', + wrapAsync(async (req, res) => { + const { path } = req.body; + const content = await ExecutorService.countFileLines(path); + res.status(200).send(content); + }), +); + +// router.post( +// '/read_file_header', +// wrapAsync(async (req, res) => { +// const { path, N } = req.body; +// try { +// const header = await ExecutorService.readFileHeader(path, N); +// res.status(200).send(header); +// } catch (error) { +// res.status(500).send({ +// error: true, +// message: error.message, +// details: error.details || error.stack, +// validation: error.validation +// }); +// } +// }), +// ); + +router.post( + '/read_file_line_context', + wrapAsync(async (req, res) => { + const { path, lineNumber, windowSize, showLines } = req.body; + try { + const context = await ExecutorService.readFileLineContext(path, lineNumber, windowSize, showLines); + res.status(200).send(context); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); + +router.post( + '/write_file', + wrapAsync(async (req, res) => { + const { path, fileContents, comment } = req.body; + try { + await ExecutorService.writeFile(path, fileContents, comment); + res.status(200).send({ message: 'File written successfully' }); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); + +router.post( + '/insert_file_content', + wrapAsync(async (req, res) => { + const { path, lineNumber, newContent, message } = req.body; + try { + await ExecutorService.insertFileContent(path, lineNumber, newContent, message); + res.status(200).send({ message: 'File written successfully' }); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); + +router.post( + '/replace_file_line', + wrapAsync(async (req, res) => { + const { path, lineNumber, newText } = req.body; + try { + const result = await ExecutorService.replaceFileLine(path, lineNumber, newText); + res.status(200).send(result); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); +router.post( + '/replace_file_chunk', + wrapAsync(async (req, res) => { + const { path, startLine, endLine, newCode } = req.body; + try { + const result = await ExecutorService.replaceFileChunk(path, startLine, endLine, newCode); + res.status(200).send(result); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); + +router.post( + '/delete_file_lines', + wrapAsync(async (req, res) => { + const { path, startLine, endLine, message } = req.body; + try { + const result = await ExecutorService.deleteFileLines(path, startLine, endLine, message); + res.status(200).send(result); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); + +router.post( + '/validate_file', + wrapAsync(async (req, res) => { + const { path } = req.body; + try { + const validationResult = await ExecutorService.validateFile(path); + res.status(200).send({ validationResult }); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }); + } + }), +); + + +router.post( + '/check_frontend_runtime_error', + wrapAsync(async (req, res) => { + try { + const result = await ExecutorService.checkFrontendRuntimeLogs(); + res.status(200).send(result); + } catch (error) { + res.status(500).send({ error: error }); + } + }), +); + + +router.post( + '/replace_code_block', + wrapAsync(async (req, res) => { + const {path, oldCode, newCode, message} = req.body; + try { + const response = await ExecutorService.replaceCodeBlock(path, oldCode, newCode, message); + res.status(200).send(response); + } catch (error) { + res.status(500).send({ + error: true, + message: error.message, + details: error.details || error.stack, + validation: error.validation + }) + } + }) +) + +router.post('/update_project_files_from_scheme', + upload.single('file'), // 'file' - name of the field in the form + async (req, res) => { + console.log('Request received'); + console.log('Headers:', req.headers); + if (!req.file) { + return res.status(400).json({ error: 'No file uploaded' }); + } + + console.log('File info:', { + originalname: req.file.originalname, + path: req.file.path, + size: req.file.size, + mimetype: req.file.mimetype + }); + + try { + console.log('Starting update process...'); + const result = await ExecutorService.updateProjectFilesFromScheme(req.file.path); + console.log('Update completed, result:', result); + + console.log('Removing temp file...'); + fs.unlinkSync(req.file.path); + console.log('Temp file removed'); + + console.log('Sending response...'); + return res.json(result); + } catch (error) { + console.error('Error in route handler:', error); + if (req.file) { + try { + fs.unlinkSync(req.file.path); + console.log('Temp file removed after error'); + } catch (unlinkError) { + console.error('Error removing temp file:', unlinkError); + } + } + console.error('Update project files error:', error); + return res.status(500).json({ + error: error.message, + stack: process.env.NODE_ENV === 'development' ? error.stack : undefined + }); + } + } +); + +router.post( + '/get_db_schema', + wrapAsync(async (req, res) => { + try { + + const jsonSchema = await ExecutorService.getDBSchema(); + res.status(200).send({ jsonSchema }); + } catch (error) { + res.status(500).send({ error: error }); + } + }), +); + +router.post( + '/execute_sql', + wrapAsync(async (req, res) => { + try { + const { query } = req.body; + const result = await ExecutorService.executeSQL(query); + res.status(200).send(result); + } catch (error) { + res.status(500).send({ error: error }); + } + }), +); + +router.post( + '/search_files', + wrapAsync(async (req, res) => { + try { + const { searchStrings } = req.body; + + if ( + typeof searchStrings !== 'string' && + !( + Array.isArray(searchStrings) && + searchStrings.every(item => typeof item === 'string') + ) + ) { + return res.status(400).send({ error: 'searchStrings must be a string or an array of strings' }); + } + + const result = await ExecutorService.searchFiles(searchStrings); + res.status(200).send(result); + } catch (error) { + res.status(500).send({ error: error.message }); + } + }), +); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/app-shell/src/routes/vcs.js b/app-shell/src/routes/vcs.js new file mode 100644 index 0000000..926498d --- /dev/null +++ b/app-shell/src/routes/vcs.js @@ -0,0 +1,40 @@ +const express = require('express'); +const wrapAsync = require('../helpers').wrapAsync; // Ваша обёртка для обработки асинхронных маршрутов +const VSC = require('../services/vcs'); +const router = express.Router(); + +router.post('/init', wrapAsync(async (req, res) => { + const result = await VSC.initRepo(); + res.status(200).send(result); +})); + +router.post('/commit', wrapAsync(async (req, res) => { + const { message, files, dev_schema } = req.body; + const result = await VSC.commitChanges(message, files, dev_schema); + res.status(200).send(result); +})); + +router.post('/log', wrapAsync(async (req, res) => { + const result = await VSC.getLog(); + res.status(200).send(result); +})); + +router.post('/rollback', wrapAsync(async (req, res) => { + const { ref } = req.body; + // const result = await VSC.checkout(ref); + const result = await VSC.revert(ref); + res.status(200).send(result); +})); + +router.post('/sync-to-stable', wrapAsync(async (req, res) => { + const result = await VSC.mergeDevIntoMaster(); + res.status(200).send(result); +})); + +router.post('/reset-dev', wrapAsync(async (req, res) => { + const result = await VSC.resetDevBranch(); + res.status(200).send(result); +})); + +router.use('/', require('../helpers').commonErrorHandler); +module.exports = router; \ No newline at end of file diff --git a/app-shell/src/services/database.js b/app-shell/src/services/database.js new file mode 100644 index 0000000..bf8f3a9 --- /dev/null +++ b/app-shell/src/services/database.js @@ -0,0 +1,88 @@ +// Database.js +const { Client } = require('pg'); +const config = require('../../../backend/src/db/db.config'); + +const env = process.env.NODE_ENV || 'development'; +const dbConfig = config[env]; + +class Database { + constructor() { + this.client = new Client({ + user: dbConfig.username, + password: dbConfig.password, + database: dbConfig.database, + host: dbConfig.host, + port: dbConfig.port + }); + + // Connect once, reuse the client + this.client.connect().catch(err => { + console.error('Error connecting to the database:', err); + throw err; + }); + } + + async executeSQL(query) { + try { + const result = await this.client.query(query); + return { + success: true, + rows: result.rows + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + // Method to fetch simple table/column info from 'information_schema' + // (You can expand this to handle constraints, indexes, etc.) + async getDBSchema(schemaName = 'public') { + try { + const tableQuery = ` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = $1 + AND table_type = 'BASE TABLE' + ORDER BY table_name + `; + + const columnQuery = ` + SELECT table_name, column_name, data_type, is_nullable + FROM information_schema.columns + WHERE table_schema = $1 + ORDER BY table_name, ordinal_position + `; + + const [tablesResult, columnsResult] = await Promise.all([ + this.client.query(tableQuery, [schemaName]), + this.client.query(columnQuery, [schemaName]), + ]); + + // Build a simple schema object: + const tables = tablesResult.rows.map(row => row.table_name); + const columnsByTable = {}; + + columnsResult.rows.forEach(row => { + const { table_name, column_name, data_type, is_nullable } = row; + if (!columnsByTable[table_name]) columnsByTable[table_name] = []; + columnsByTable[table_name].push({ column_name, data_type, is_nullable }); + }); + + // Combine tables with their columns + return tables.map(table => ({ + table, + columns: columnsByTable[table] || [], + })); + } catch (error) { + console.error('Error fetching schema:', error); + throw error; + } + } + + async close() { + await this.client.end(); + } +} + +module.exports = new Database(); diff --git a/app-shell/src/services/executor.js b/app-shell/src/services/executor.js new file mode 100644 index 0000000..eecb869 --- /dev/null +++ b/app-shell/src/services/executor.js @@ -0,0 +1,1206 @@ +const fs = require('fs').promises; +const os = require('os'); +const path = require('path'); +const AdmZip = require('adm-zip'); +const { exec } = require('child_process'); +const util = require('util'); +const ProjectEventsService = require('./project-events'); +const config = require('../config.js'); +// Babel Parser for JS/TS/TSX +const babelParser = require('@babel/parser'); +const babelParse = babelParser.parse; + +// Local App DB Connection +const database = require('./database'); + +// PostCSS for CSS +const postcss = require('postcss'); + +const execAsync = util.promisify(exec); + +module.exports = class ExecutorService { + static async readProjectTree(directoryPath) { + const paths = { + frontend: '../../../frontend', + backend: '../../../backend', + default: '../../../' + }; + + try { + const publicDir = path.join(__dirname, paths[directoryPath] || directoryPath || paths.default); + + return await getDirectoryTree(publicDir); + } catch (error) { + console.error('Error reading directory:', error); + + throw error; + } + } + + static async readFileContents(filePath, showLines) { + try { + const fullPath = path.join(__dirname, filePath); + const content = await fs.readFile(fullPath, 'utf8'); + + if (showLines) { + const lines = content.split('\n'); + + const lineObject = {}; + lines.forEach((line, index) => { + lineObject[index + 1] = line; + }); + + return lineObject; + } else { + return content; + } + } catch (error) { + console.error('Error reading file:', error); + throw error; + } + } + + static async countFileLines(filePath) { + try { + const fullPath = path.join(__dirname, filePath); + + // Check file exists + await fs.access(fullPath); + + // Read file content + const content = await fs.readFile(fullPath, 'utf8'); + + // Split by newline and count + const lines = content.split('\n'); + + return { + success: true, + lineCount: lines.length + }; + } catch (error) { + console.error('Error counting file lines:', error); + return { + success: false, + message: error.message + }; + } + } + + // static async readFileHeader(filePath, N = 30) { + // try { + // const fullPath = path.join(__dirname, filePath); + // const content = await fs.readFile(fullPath, 'utf8'); + // const lines = content.split('\n'); + // + // if (lines.length < N) { + // return { error: `File has less than ${N} lines` }; + // } + // + // const headerLines = lines.slice(0, Math.min(50, lines.length)); + // + // const lineObject = {}; + // headerLines.forEach((line, index) => { + // lineObject[index + 1] = line; + // }); + // + // return lineObject; + // } catch (error) { + // console.error('Error reading file header:', error); + // throw error; + // } + // } + + static async readFileLineContext(filePath, lineNumber, windowSize, showLines) { + try { + const fullPath = path.join(__dirname, filePath); + const content = await fs.readFile(fullPath, 'utf8'); + const lines = content.split('\n'); + + const start = Math.max(0, lineNumber - windowSize); + const end = Math.min(lines.length, lineNumber + windowSize + 1); + + const contextLines = lines.slice(start, end); + + if (showLines) { + const lineObject = {}; + contextLines.forEach((line, index) => { + lineObject[start + index + 1] = line; + }); + + return lineObject; + } else { + return contextLines.join('\n'); + } + } catch (error) { + console.error('Error reading file line context:', error); + throw error; + } + } + + static async validateFile(filePath) { + console.log('Validating file:', filePath); + + // Read file content + let content; + try { + content = await fs.readFile(filePath, 'utf8'); + } catch (err) { + throw new Error(`Could not read file: ${filePath}\n${err.message}`); + } + + // Determine file extension + let ext = path.extname(filePath).toLowerCase(); + if (ext === '.temp') { + ext = path.extname(filePath.slice(0, -5)).toLowerCase(); + } + + try { + switch (ext) { + case '.js': + case '.ts': + case '.tsx': { + // Parse JS/TS/TSX with Babel + babelParse(content, { + sourceType: 'module', + // plugins array covers JS, TS, TSX, and optional JS flavors + plugins: ['jsx', 'typescript'] + }); + break; + } + + case '.css': { + // Parse CSS with PostCSS + postcss.parse(content); + break; + } + + default: { + // If the extension isn't recognized, assume it's "valid" + // or you could throw an error to force a known extension + console.warn(`No validation implemented for extension "${ext}". Skipping syntax check.`); + } + } + + // If parsing succeeded, return true + return true; + + } catch (parseError) { + // Rethrow parse errors with a friendlier message + throw parseError; + } + } + + static async checkFrontendRuntimeLogs() { + const frontendLogPath = '../frontend/json/runtimeError.json'; + + try { + // Check if file exists + try { + console.log('Accessing frontend logs:', frontendLogPath); + await fs.access(frontendLogPath); + } catch (error) { + console.log('Frontend logs not found:', error); + // File doesn't exist - return empty object + return {runtime_error: {}}; + } + + // File exists, try to read it + try { + // Read the entire file instead of using tail + const fileContent = await fs.readFile(frontendLogPath, 'utf8'); + console.log('Reading frontend logs:', fileContent); + + // Handle empty file + if (!fileContent || fileContent.trim() === '') { + return {runtime_error: {}}; + } + + // Parse JSON content + const runtime_error = JSON.parse(fileContent); + + console.log('Parsed frontend logs:', runtime_error); + return {runtime_error}; + } catch (error) { + // Error reading or parsing file + console.error('Error reading frontend runtime logs:', error); + return {runtime_error: {}}; + } + } catch (error) { + // Unexpected error + console.log('Error checking frontend logs:', error); + return {runtime_error: {}}; + } + } + + static async writeFile(filePath, fileContents, comment) { + try { + console.log(comment) + const fullPath = path.join(__dirname, filePath); + + // Write to a temp file first + const tempPath = `${fullPath}.temp`; + await fs.writeFile(tempPath, fileContents, 'utf8'); + + // Validate the temp file + await this.validateFile(tempPath); + + // Rename temp file to original path + await fs.rename(tempPath, fullPath); + + return true; + } catch (error) { + console.error('Error writing file:', error); + throw error; + } + } + + static async insertFileContent(filePath, lineNumber, newContent, message) { + try { + const fullPath = path.join(__dirname, filePath); + + // Check file exists + await fs.access(fullPath); + + // Read and split by line + const content = await fs.readFile(fullPath, 'utf8'); + const lines = content.split('\n'); + + // Ensure lineNumber is within [1 ... lines.length + 1] + // 1 means "insert at the very first line" + // lines.length + 1 means "append at the end" + if (lineNumber < 1) { + lineNumber = 1; + } + if (lineNumber > lines.length + 1) { + lineNumber = lines.length + 1; + } + + // Convert to 0-based index + const insertIndex = lineNumber - 1; + + // Prepare preview + const preview = { + insertionLine: lineNumber, + insertedLines: newContent.split('\n') + }; + + // Insert newContent lines at the specified index + lines.splice(insertIndex, 0, ...newContent.split('\n')); + + // Write changes to a temp file first + const updatedContent = lines.join('\n'); + const tempPath = `${fullPath}.temp`; + await fs.writeFile(tempPath, updatedContent, 'utf8'); + + await this.validateFile(tempPath); + + // Rename temp file to original path + await fs.rename(tempPath, fullPath); + + return { + success: true + }; + + } catch (error) { + console.error('Error inserting file content:', error); + throw error; + } + } + + static async replaceFileLine(filePath, lineNumber, newText, message = null) { + const fullPath = path.join(__dirname, filePath); + try { + + try { + await fs.access(fullPath); + } catch (error) { + throw new Error(`File not found: ${filePath}`); + } + + const content = await fs.readFile(fullPath, 'utf8'); + const lines = content.split('\n'); + + if (lineNumber < 1 || lineNumber > lines.length) { + throw new Error(`Invalid line number: ${lineNumber}. File has ${lines.length} lines`); + } + + if (typeof newText !== 'string') { + throw new Error('New text must be a string'); + } + + const preview = { + oldLine: lines[lineNumber - 1], + newLine: newText, + lineNumber: lineNumber + }; + + lines[lineNumber - 1] = newText; + const newContent = lines.join('\n'); + const tempPath = `${fullPath}.temp`; + await fs.writeFile(tempPath, newContent, 'utf8'); + + await this.validateFile(tempPath); + + await fs.rename(tempPath, fullPath); + + return { + success: true + }; + + } catch (error) { + console.error('Error updating file line:', error); + + try { + await fs.unlink(`${fullPath}.temp`); + } catch { + } + + throw { + error: error, + message: error.message, + details: error.stack + }; + } + } + + static async replaceFileChunk(filePath, startLine, endLine, newCode) { + try { + // Check if this is a single-line change + const newCodeLines = newCode.split('\n'); + if (newCodeLines.length === 1 && endLine === startLine) { + // Redirect to replace_file_line + return await this.replaceFileLine(filePath, startLine, newCode); + } + + const fullPath = path.join(__dirname, filePath); + + // Check if file exists + try { + await fs.access(fullPath); + } catch (error) { + throw new Error(`File not found: ${filePath}`); + } + + const content = await fs.readFile(fullPath, 'utf8'); + const lines = content.split('\n'); + + // Adjust line numbers to array indices (subtract 1) + const startIndex = startLine - 1; + const endIndex = endLine - 1; + + // Validate input parameters + if (startIndex < 0 || endIndex >= lines.length || startIndex > endIndex) { + throw new Error(`Invalid line range: ${startLine}-${endLine}. File has ${lines.length} lines`); + } + + // Check type of new code + if (typeof newCode !== 'string') { + throw new Error('New code must be a string'); + } + + // Create changes preview + const preview = { + oldLines: lines.slice(startIndex, endIndex + 1), + newLines: newCode.split('\n'), + startLine, + endLine + }; + + // Apply changes to temp file first + lines.splice(startIndex, endIndex - startIndex + 1, ...newCode.split('\n')); + const newContent = lines.join(os.EOL); + const tempPath = `${fullPath}.temp`; + await fs.writeFile(tempPath, newContent, 'utf8'); + await this.validateFile(tempPath); + // Apply changes if all validations passed + await fs.rename(tempPath, fullPath); + + return { + success: true + }; + + } catch (error) { + console.error('Error updating file slice:', error); + + // Clean up temp file if exists + try { + await fs.unlink(`${fullPath}.temp`); + } catch { + } + + throw { + error: error, + message: error.message, + details: error.details || error.stack + }; + } + } + + static async replaceCodeBlock(filePath, oldCode, newCode, message) { + try { + console.log(message); + const fullPath = path.join(__dirname, filePath); + + // Check file exists + await fs.access(fullPath); + + // Read file content + let content = await fs.readFile(fullPath, 'utf8'); + + // A small helper to unify line breaks to just `\n` + const unifyLineBreaks = (str) => str.replace(/\r\n/g, '\n'); + + // Normalize line breaks in file content, oldCode, and newCode + content = unifyLineBreaks(content); + oldCode = unifyLineBreaks(oldCode); + newCode = unifyLineBreaks(newCode); + + // Optional: Trim trailing spaces or handle other whitespace normalization if needed + // oldCode = oldCode.trim(); + // newCode = newCode.trim(); + + // Check if oldCode actually exists in the content + const index = content.indexOf(oldCode); + if (index === -1) { + return { + success: false, + message: 'Old code not found in file.' + }; + } + + // Create a preview before replacing + const preview = { + oldCodeSnippet: oldCode, + newCodeSnippet: newCode + }; + + // Perform replacement (single occurrence). For multiple, use replaceAll or a loop. + // If you want a global replacement, consider: + // content = content.split(oldCode).join(newCode); + content = content.replace(oldCode, newCode); + + // Write to a temp file first + const tempPath = `${fullPath}.temp`; + await fs.writeFile(tempPath, content, 'utf8'); + + await this.validateFile(tempPath); + // Rename temp file to original + await fs.rename(tempPath, fullPath); + + return { + success: true + }; + + } catch (error) { + console.error('Error replacing code:', error); + return { + error: error, + message: error.message, + details: error.details || error.stack + }; + } + } + + //todo add validation + static async deleteFileLines(filePath, startLine, endLine, veryShortDescription) { + try { + const fullPath = path.join(__dirname, filePath); + + // Check if file exists + await fs.access(fullPath); + + // Read file content + const content = await fs.readFile(fullPath, 'utf8'); + const lines = content.split('\n'); + + // Convert to zero-based indices + const startIndex = startLine - 1; + const endIndex = endLine - 1; + + // Validate range + if (startIndex < 0 || endIndex >= lines.length || startIndex > endIndex) { + throw new Error( + `Invalid line range: ${startLine}-${endLine}. File has ${lines.length} lines` + ); + } + + // Prepare a preview of the lines being deleted + const preview = { + deletedLines: lines.slice(startIndex, endIndex + 1), + startLine, + endLine + }; + + // Remove lines + lines.splice(startIndex, endIndex - startIndex + 1); + + // Join remaining lines and write to a temporary file + const newContent = lines.join('\n'); + const tempPath = `${fullPath}.temp`; + await fs.writeFile(tempPath, newContent, 'utf8'); + + await this.validateFile(tempPath); + // Rename temp file to original + await fs.rename(tempPath, fullPath); + + return { + success: true + }; + + } catch (error) { + console.error('Error deleting file lines:', error); + return { + error: error, + message: error.message, + details: error.details || error.stack + }; + } + } + + static async validateTypeScript(filePath, content = null) { + try { + // Basic validation of JSX syntax + const jsxErrors = []; + + if (content !== null) { + // Check for matching braces + if ((content.match(/{/g) || []).length !== (content.match(/}/g) || []).length) { + jsxErrors.push("Unmatched curly braces"); + } + + // Check for invalid syntax in JSX attributes + if (content.includes('label={')) { + if (!content.match(/label={[^}]+}/)) { + jsxErrors.push("Invalid label attribute syntax"); + } + } + + if (jsxErrors.length > 0) { + return { + valid: false, + errors: jsxErrors.map(error => ({ + code: 'JSX_SYNTAX_ERROR', + severity: 'error', + location: '', + message: error + })) + }; + } + } + + return { + valid: true, + errors: [], + errorCount: 0, + warningCount: 0 + }; + + } catch (error) { + console.error('TypeScript validation error:', error); + return { + valid: false, + errors: [{ + code: 'VALIDATION_FAILED', + severity: 'error', + location: '', + message: `TypeScript validation error: ${error.message}` + }], + errorCount: 1, + warningCount: 0 + }; + } + } + + static async validateBackendFiles(backendPath) { + try { + // Check for syntax errors + await execAsync(`node --check ${backendPath}/src/index.js`); + + // Try to run the code in a test environment + const testProcess = exec( + 'NODE_ENV=test node -e "try { require(\'./src/index.js\') } catch(e) { console.error(e); process.exit(1) }"', + {cwd: backendPath} + ); + + return new Promise((resolve) => { + let output = ''; + let error = ''; + + testProcess.stdout.on('data', (data) => { + output += data; + }); + + testProcess.stderr.on('data', (data) => { + error += data; + }); + + testProcess.on('close', (code) => { + if (code === 0) { + resolve({valid: true}); + } else { + resolve({ + valid: false, + error: error || output + }); + } + }); + + // Timeout on validation + setTimeout(() => { + testProcess.kill(); + resolve({ + valid: true, + warning: 'Validation timeout, but no immediate errors found' + }); + }, 5000); + }); + } catch (error) { + return { + valid: false, + error: error.message + }; + } + } + + static async createBackup(ROOT_PATH) { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const backupDir = path.join(ROOT_PATH, 'backups', timestamp); + + try { + await fs.mkdir(path.join(ROOT_PATH, 'backups'), {recursive: true}); + + const dirsToBackup = ['frontend', 'backend']; + + for (const dir of dirsToBackup) { + const sourceDir = path.join(ROOT_PATH, dir); + const targetDir = path.join(backupDir, dir); + + await fs.mkdir(targetDir, {recursive: true}); + + await execAsync( + `cd "${sourceDir}" && ` + + `find . -type f -not -path "*/node_modules/*" -not -path "*/\\.*" | ` + + `while read file; do ` + + `mkdir -p "${targetDir}/$(dirname "$file")" && ` + + `cp "$file" "${targetDir}/$file"; ` + + `done` + ); + } + + console.log('Backup created at:', backupDir); + return backupDir; + } catch (error) { + console.error('Error creating backup:', error); + throw error; + } + } + + static async restoreFromBackup(backupDir, ROOT_PATH) { + try { + console.log('Restoring from backup:', backupDir); + await execAsync(`rm -rf ${ROOT_PATH}/backend/*`); + await execAsync(`cp -r ${backupDir}/* ${ROOT_PATH}/backend/`); + return true; + } catch (error) { + console.error('Error restoring from backup:', error); + throw error; + } + } + + static async updateProjectFilesFromScheme(zipFilePath) { + const MAX_FILE_SIZE = 10 * 1024 * 1024; + const ROOT_PATH = path.join(__dirname, '../../../'); + + try { + console.log('Checking file access...'); + await fs.access(zipFilePath); + + console.log('Getting file stats...'); + const stats = await fs.stat(zipFilePath); + console.log('File size:', stats.size); + + if (stats.size > MAX_FILE_SIZE) { + console.log('File size exceeds limit'); + return {success: false, error: 'File size exceeds limit'}; + } + + // Copying zip file to /tmp + const tempZipPath = path.join('/tmp', path.basename(zipFilePath)); + await fs.copyFile(zipFilePath, tempZipPath); + + // Launching background update process + const servicesUpdate = (async () => { + try { + console.log('Stopping services...'); + + // await ProjectEventsService.sendEvent('SERVICE_STOP_STARTED', { + // message: 'Stopping services', + // timestamp: new Date().toISOString() + // }); + + await stopServices(); + + // await ProjectEventsService.sendEvent('SERVICE_STOP_COMPLETED', { + // message: 'Services stopped successfully', + // timestamp: new Date().toISOString() + // }); + + console.log('Creating zip instance...'); + const zip = new AdmZip(tempZipPath); + + console.log('Extracting files to:', ROOT_PATH); + zip.extractAllTo(ROOT_PATH, true); + console.log('Files extracted'); + + const removedFilesPath = path.join(ROOT_PATH, 'removed_files.json'); + try { + await fs.access(removedFilesPath); + const removedFilesContent = await fs.readFile(removedFilesPath, 'utf8'); + const filesToRemove = JSON.parse(removedFilesContent); + await removeFiles(filesToRemove, ROOT_PATH); + + await fs.unlink(removedFilesPath); + } catch (error) { + console.log('No removed files to process or error accessing removed_files.json:', error); + } + + // Remove temp zip file + await fs.unlink(tempZipPath); + + // await ProjectEventsService.sendEvent('SERVICE_START_STARTED', { + // message: 'Starting services', + // timestamp: new Date().toISOString() + // }); + + // Start services after a delay + setTimeout(async () => { + try { + await startServices(); + console.log('Services started successfully'); + + await ProjectEventsService.sendEvent('SERVICE_START_COMPLETED', { + message: 'All files have been successfully retrieved and applied.', + timestamp: new Date().toISOString() + }); + } catch (e) { + console.error('Failed to start services:', e); + } + }, 3000); + + } catch (error) { + console.error('Error in service update process:', error); + } + })(); + + servicesUpdate.catch(error => { + console.error('Background update process failed:', error); + }); + + console.log('Returning immediate response'); + + return { + success: true, + message: 'Update process initiated' + }; + + } catch (error) { + console.error('Critical error in updateProjectFilesFromScheme:', error); + return { + success: false, + error: error.message + }; + } + } + + static async getDBSchema() { + try { + return await database.getDBSchema(); + } catch (error) { + console.error('Error reading schema:', error); + throw { + error: error, + message: error.message, + details: error.details || error.stack + }; + } + } + + static async executeSQL(query) { + try { + return await database.executeSQL(query); + } catch (error) { + console.error('Error executing query:', error); + throw { + error: error, + message: error.message, + details: error.details || error.stack + }; + } + } + + static async stopServices() { + return await stopServices(); + } + + static async startServices() { + return await startServices(); + } + + static async checkServicesStatus() { + return await checkStatus(); + } + + static async searchFiles(searchStrings) { + const results = {}; + const ROOT_PATH = path.join(__dirname, '../../../'); + const directories = [`${ROOT_PATH}backend/`, `${ROOT_PATH}frontend/`]; + const excludeDirs = ['node_modules', 'build', 'app_shell']; + + if (!Array.isArray(searchStrings)) { + searchStrings = [searchStrings]; + } + + for (const searchString of searchStrings) { + try { + for (const directoryPath of directories) { + const findCommand = `find '${directoryPath}' -type f ${excludeDirs.map(dir => `-not -path "*/${dir}/*"`).join(' ')} -print | xargs grep -nH -C 1 -e '${searchString}'`; + + try { + const { stdout } = await execAsync(findCommand); + + const lines = stdout.trim().split('\n').filter(line => line !== ''); + const searchResults = {}; + // searchResults['__raw_lines__'] = lines; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const parts = line.split(':'); + let filePath = ''; + let lineNumberStr = ''; + let content = ''; + let relativeFilePath = ''; + let lineNum = null; + + if (parts.length >= 3 && !parts[0].includes('-')) { + filePath = parts.shift(); + lineNumberStr = parts.shift(); + content = parts.join(':').trim(); + relativeFilePath = filePath.replace(`${ROOT_PATH}`, ''); + lineNum = parseInt(lineNumberStr, 10) + 1; + } else { + content = line.trim(); + } + + const context = []; + if (i > 0 && lines[i - 1].includes(':')) { + const prevLineParts = lines[i - 1].split(':'); + if (prevLineParts.length >= 3 && !prevLineParts[0].includes('-')) { + prevLineParts.shift(); + prevLineParts.shift(); + context.push(prevLineParts.join(':').trim()); + } else { + context.push(lines[i - 1].trim()); + } + } + context.push(content); + if (i < lines.length - 1 && lines[i + 1].includes(':')) { + const nextLineParts = lines[i + 1].split(':'); + if (nextLineParts.length >= 3 && !nextLineParts[0].includes('-')) { + nextLineParts.shift(); + nextLineParts.shift(); + context.push(nextLineParts.join(':').trim()); + } else { + context.push(lines[i + 1].trim()); + } + } + + if (relativeFilePath && !searchResults[relativeFilePath]) { + searchResults[relativeFilePath] = []; + } + if (relativeFilePath) { + searchResults[relativeFilePath].push({ + lineNumber: lineNum, + context: context.join('\n'), + // __filePathAndLine__: filePath + ':' + lineNumberStr + ':' + content, + }); + } + } + + if (!results[searchString]) { + results[searchString] = {}; + } + Object.assign(results[searchString], searchResults); + } catch (err) { + if (!err.message.includes('No such file or directory') && !err.stderr.includes('No such file or directory')) { + console.error(`Error using find/grep for "${searchString}" in ${directoryPath}:`, err); + } + } + } + } catch (error) { + console.error(`Error searching for "${searchString}":`, error); + results[searchString] = { error: error.message }; + } + } + + return results; + } + +} + +async function getDirectoryTree(dirPath) { + const entries = await fs.readdir(dirPath, { withFileTypes: true }); + const result = {}; + + for (const entry of entries) { + const fullPath = path.join(dirPath, entry.name); + + if (entry.isDirectory() && ( + entry.name === 'node_modules' || + entry.name === 'app-shell' || + entry.name === '.git' || + entry.name === '.idea' + )) { + continue; + } + + const relativePath = fullPath.replace('/app', ''); + + if (entry.isDirectory()) { + const subTree = await getDirectoryTree(fullPath); + Object.keys(subTree).forEach(key => { + result[key.replace('/app', '')] = subTree[key]; + }); + } else { + const fileContent = await fs.readFile(fullPath, 'utf8'); + const lineCount = fileContent.split('\n').length; + result[relativePath] = lineCount; + } + } + + return result; +} + +async function stopServices() { + try { + console.log('Finding service processes...'); + // await ProjectEventsService.sendEvent('SERVICE_STOP_INITIATED', { + // message: 'Initiating service stop', + // timestamp: new Date().toISOString() + // }); + // Frontend stopping + const { stdout: frontendProcess } = await execAsync("ps -o pid,cmd | grep '[n]ext-server' | awk '{print $1}'"); + if (frontendProcess.trim()) { + console.log('Stopping frontend, pid:', frontendProcess.trim()); + + // await ProjectEventsService.sendEvent('FRONTEND_STOP_STARTED', { + // message: `Stopping frontend, pid: ${frontendProcess.trim()}`, + // timestamp: new Date().toISOString() + // }); + + // await execAsync(`kill -15 ${frontendProcess.trim()}`); + + // await ProjectEventsService.sendEvent('FRONTEND_STOP_COMPLETED', { + // message: 'Frontend stopped successfully', + // timestamp: new Date().toISOString() + // }); + } + + // Backend stopping + const { stdout: backendProcess } = await execAsync("ps -o pid,cmd | grep '[n]ode ./src/index.js' | grep -v app-shell | awk '{print $1}'"); + if (backendProcess.trim()) { + console.log('Stopping backend, pid:', backendProcess.trim()); + + // await ProjectEventsService.sendEvent('BACKEND_STOP_STARTED', { + // message: `Stopping backend, pid: ${backendProcess.trim()}`, + // timestamp: new Date().toISOString() + // }); + + // await execAsync(`kill -15 ${backendProcess.trim()}`); + + // await ProjectEventsService.sendEvent('BACKEND_STOP_COMPLETED', { + // message: 'Backend stopped successfully', + // timestamp: new Date().toISOString() + // }); + } + + await new Promise(resolve => setTimeout(resolve, 4000)); + + + // await ProjectEventsService.sendEvent('SERVICE_STOP_COMPLETED', { + // message: 'All services stopped successfully', + // timestamp: new Date().toISOString() + // }); + + return { success: true }; + } catch (error) { + console.error('Error stopping services:', error); + + await ProjectEventsService.sendEvent('SERVICE_STOP_FAILED', { + message: 'Error stopping services', + error: error.message, + timestamp: new Date().toISOString() + }); + + return { success: false, error: error.message }; + } +} + +async function startServices() { + try { + console.log('Starting services...'); + // await ProjectEventsService.sendEvent('SERVICE_START_INITIATED', { + // message: 'Initiating service start', + // timestamp: new Date().toISOString() + // }); + + // await ProjectEventsService.sendEvent('FRONTEND_START_STARTED', { + // message: 'Starting frontend service', + // timestamp: new Date().toISOString() + // }); + // await execAsync('yarn --cwd /app/frontend dev &'); + // await ProjectEventsService.sendEvent('FRONTEND_START_COMPLETED', { + // message: 'Frontend service started successfully', + // timestamp: new Date().toISOString() + // }); + + // await ProjectEventsService.sendEvent('BACKEND_START_STARTED', { + // message: 'Starting backend service', + // timestamp: new Date().toISOString() + // }); + // await execAsync('yarn --cwd /app/backend start &'); + // await ProjectEventsService.sendEvent('BACKEND_START_COMPLETED', { + // message: 'Backend service started successfully', + // timestamp: new Date().toISOString() + // }); + + // await ProjectEventsService.sendEvent('SERVICE_START_COMPLETED', { + // message: 'All services started successfully', + // timestamp: new Date().toISOString() + // }); + + return { success: true }; + } catch (error) { + console.error('Error starting services:', error); + await ProjectEventsService.sendEvent('SERVICE_START_FAILED', { + message: 'Error starting services', + error: error.message, + timestamp: new Date().toISOString() + }); + return { success: false, error: error.message }; + } +} + +async function checkStatus() { + try { + const { stdout } = await execAsync('ps aux'); + return { + success: true, + frontendRunning: stdout.includes('next-server'), + backendRunning: stdout.includes('nodemon') && stdout.includes('/app/backend'), + nginxRunning: stdout.includes('nginx: master process') + }; + } catch (error) { + return { + success: false, + error: error.message + }; + } +} + +async function validateJSXSyntax(code) { + // Define validation rules for JSX + const rules = [ + { + // JSX attribute with expression + pattern: /^[a-zA-Z][a-zA-Z0-9]*={.*}$/, + message: 'Invalid JSX attribute syntax' + }, + { + // Invalid sequences + pattern: /,{2,}/, + message: 'Invalid character sequence detected', + shouldNotMatch: true + }, + { + // Ternary expressions + pattern: /^[a-zA-Z][a-zA-Z0-9]*={[\w\s]+\?[^}]+:[^}]+}$/, + message: 'Invalid ternary expression in JSX' + } + ]; + + // Validate each line + const lines = code.split('\n'); + for (const line of lines) { + const trimmedLine = line.trim(); + + // Skip empty lines + if (!trimmedLine) continue; + + // Check each rule + for (const rule of rules) { + if (rule.shouldNotMatch) { + // For patterns that should not be present + if (rule.pattern.test(trimmedLine)) { + return { + valid: false, + errors: [{ + code: 'JSX_SYNTAX_ERROR', + severity: 'error', + location: '', + message: rule.message + }] + }; + } + } else { + // For patterns that should match + if (trimmedLine.includes('=') && !rule.pattern.test(trimmedLine)) { + return { + valid: false, + errors: [{ + code: 'JSX_SYNTAX_ERROR', + severity: 'error', + location: '', + message: rule.message + }] + }; + } + } + } + + // Additional JSX-specific checks + if ((trimmedLine.match(/{/g) || []).length !== (trimmedLine.match(/}/g) || []).length) { + return { + valid: false, + errors: [{ + code: 'JSX_SYNTAX_ERROR', + severity: 'error', + location: '', + message: 'Unmatched curly braces in JSX' + }] + }; + } + } + + // If all checks pass + return { + valid: true, + errors: [] + }; +} + +async function removeFiles(files, rootPath) { + try { + for (const file of files) { + const fullPath = path.join(rootPath, file); + try { + await fs.unlink(fullPath); + console.log(`File removed: ${fullPath}`); + } catch (error) { + console.error(`Error when trying to delete a file ${fullPath}:`, error); + } + } + } catch (error) { + console.error('Error removing files:', error); + throw error; + } +} \ No newline at end of file diff --git a/app-shell/src/services/notifications/errors/forbidden.js b/app-shell/src/services/notifications/errors/forbidden.js new file mode 100644 index 0000000..192fa10 --- /dev/null +++ b/app-shell/src/services/notifications/errors/forbidden.js @@ -0,0 +1,16 @@ +const { getNotification, isNotification } = require('../helpers'); + +module.exports = class ForbiddenError extends Error { + constructor(messageCode) { + let message; + + if (messageCode && isNotification(messageCode)) { + message = getNotification(messageCode); + } + + message = message || getNotification('errors.forbidden.message'); + + super(message); + this.code = 403; + } +}; diff --git a/app-shell/src/services/notifications/errors/validation.js b/app-shell/src/services/notifications/errors/validation.js new file mode 100644 index 0000000..464550c --- /dev/null +++ b/app-shell/src/services/notifications/errors/validation.js @@ -0,0 +1,16 @@ +const { getNotification, isNotification } = require('../helpers'); + +module.exports = class ValidationError extends Error { + constructor(messageCode) { + let message; + + if (messageCode && isNotification(messageCode)) { + message = getNotification(messageCode); + } + + message = message || getNotification('errors.validation.message'); + + super(message); + this.code = 400; + } +}; diff --git a/app-shell/src/services/notifications/helpers.js b/app-shell/src/services/notifications/helpers.js new file mode 100644 index 0000000..1c3a60f --- /dev/null +++ b/app-shell/src/services/notifications/helpers.js @@ -0,0 +1,30 @@ +const _get = require('lodash/get'); +const errors = require('./list'); + +function format(message, args) { + if (!message) { + return null; + } + + return message.replace(/{(\d+)}/g, function (match, number) { + return typeof args[number] != 'undefined' ? args[number] : match; + }); +} + +const isNotification = (key) => { + const message = _get(errors, key); + return !!message; +}; + +const getNotification = (key, ...args) => { + const message = _get(errors, key); + + if (!message) { + return key; + } + + return format(message, args); +}; + +exports.getNotification = getNotification; +exports.isNotification = isNotification; diff --git a/app-shell/src/services/notifications/list.js b/app-shell/src/services/notifications/list.js new file mode 100644 index 0000000..a0a1613 --- /dev/null +++ b/app-shell/src/services/notifications/list.js @@ -0,0 +1,100 @@ +const errors = { + app: { + title: 'test', + }, + + auth: { + userDisabled: 'Your account is disabled', + forbidden: 'Forbidden', + unauthorized: 'Unauthorized', + userNotFound: `Sorry, we don't recognize your credentials`, + wrongPassword: `Sorry, we don't recognize your credentials`, + weakPassword: 'This password is too weak', + emailAlreadyInUse: 'Email is already in use', + invalidEmail: 'Please provide a valid email', + passwordReset: { + invalidToken: 'Password reset link is invalid or has expired', + error: `Email not recognized`, + }, + passwordUpdate: { + samePassword: `You can't use the same password. Please create new password`, + }, + userNotVerified: `Sorry, your email has not been verified yet`, + emailAddressVerificationEmail: { + invalidToken: 'Email verification link is invalid or has expired', + error: `Email not recognized`, + }, + }, + + iam: { + errors: { + userAlreadyExists: 'User with this email already exists', + userNotFound: 'User not found', + disablingHimself: `You can't disable yourself`, + revokingOwnPermission: `You can't revoke your own owner permission`, + deletingHimself: `You can't delete yourself`, + emailRequired: 'Email is required', + }, + }, + + importer: { + errors: { + invalidFileEmpty: 'The file is empty', + invalidFileExcel: 'Only excel (.xlsx) files are allowed', + invalidFileUpload: + 'Invalid file. Make sure you are using the last version of the template.', + importHashRequired: 'Import hash is required', + importHashExistent: 'Data has already been imported', + userEmailMissing: 'Some items in the CSV do not have an email', + }, + }, + + errors: { + forbidden: { + message: 'Forbidden', + }, + validation: { + message: 'An error occurred', + }, + searchQueryRequired: { + message: 'Search query is required', + }, + }, + + emails: { + invitation: { + subject: `You've been invited to {0}`, + body: ` +

Hello,

+

You've been invited to {0} set password for your {1} account.

+

{2}

+

Thanks,

+

Your {0} team

+ `, + }, + emailAddressVerification: { + subject: `Verify your email for {0}`, + body: ` +

Hello,

+

Follow this link to verify your email address.

+

{0}

+

If you didn't ask to verify this address, you can ignore this email.

+

Thanks,

+

Your {1} team

+ `, + }, + passwordReset: { + subject: `Reset your password for {0}`, + body: ` +

Hello,

+

Follow this link to reset your {0} password for your {1} account.

+

{2}

+

If you didn't ask to reset your password, you can ignore this email.

+

Thanks,

+

Your {0} team

+ `, + }, + }, +}; + +module.exports = errors; diff --git a/app-shell/src/services/project-events.js b/app-shell/src/services/project-events.js new file mode 100644 index 0000000..dabc32d --- /dev/null +++ b/app-shell/src/services/project-events.js @@ -0,0 +1,67 @@ +const axios = require('axios'); +const config = require('../config.js'); + +class ProjectEventsService { + /** + * Sends a project event to the Rails backend + * + * @param {string} eventType - Type of the event + * @param {object} payload - Event payload data + * @param {object} options - Additional options + * @param {string} [options.conversationId] - Optional conversation ID + * @param {boolean} [options.isError=false] - Whether this is an error event + * @returns {Promise} - Response from the webhook + */ + static async sendEvent(eventType, payload = {}, options = {}) { + try { + console.log(`[DEBUG] Sending project event: ${eventType}`); + + const webhookUrl = `https://flatlogic.com/projects/events_webhook`; + + // Prepare the event data + const eventData = { + project_uuid: config.project_uuid, + event_type: eventType, + payload: { + ...payload, + message: `[APP] ${payload.message}`, + is_error: options.isError || false, + system_message: true, + is_command_info: true + } + }; + + // Add conversation ID if provided + if (options.conversationId) { + eventData.conversation_id = options.conversationId; + } + + const headers = { + 'Content-Type': 'application/json', + 'x-project-uuid': config.project_uuid + }; + + console.log(`[DEBUG] Event data: ${JSON.stringify(eventData)}`); + + const response = await axios.post(webhookUrl, eventData, { headers }); + + console.log(`[DEBUG] Event sent successfully, status: ${response.status}`); + return response.data; + } catch (error) { + console.error(`[ERROR] Failed to send project event: ${error.message}`); + if (error.response) { + console.error(`[ERROR] Response status: ${error.response.status}`); + console.error(`[ERROR] Response data: ${JSON.stringify(error.response.data)}`); + } + + // Don't throw the error, just return a failed status + // This prevents errors in the event service from breaking app functionality + return { + success: false, + error: error.message + }; + } + } +} + +module.exports = ProjectEventsService; \ No newline at end of file diff --git a/app-shell/src/services/vcs.js b/app-shell/src/services/vcs.js new file mode 100644 index 0000000..6235c57 --- /dev/null +++ b/app-shell/src/services/vcs.js @@ -0,0 +1,1205 @@ +const util = require('util'); +const exec = util.promisify(require('child_process').exec); +const path = require('path'); +const { promises: fs } = require("fs"); +const axios = require('axios'); +const config = require('../config.js'); + +const ROOT_PATH = '/app'; +const MAX_BUFFER = 1024 * 1024 * 50; +const GITEA_DOMAIN = config.gitea_domain; +const USERNAME = config.gitea_username; +const API_TOKEN = config.gitea_api_token; +const GITHUB_REPO_URL = config.github_repo_url; +const GITHUB_TOKEN = config.github_token; + +const devSchemaFilePath = path.join(ROOT_PATH, '/app-shell/src/_schema.json'); + +class VCS { + static isInitRepoRunning = false; + // Main method – controller of the repository initialization process + static async initRepo(projectId = 'test') { + if (VCS.isInitRepoRunning) { + console.warn('[WARNING] initRepo is already running. Skipping.'); + return; + } + VCS.isInitRepoRunning = true; + try { + console.log(`[DEBUG] Starting repository initialization for project "${projectId}"...`); + + await this._waitForGitLockRelease(path.join(ROOT_PATH, '.git')); + // await this._removeGitLockIfExists(path.join(ROOT_PATH, '.git')); + console.log('[DEBUG] Git lock released, proceeding with initialization...'); + + if (GITHUB_REPO_URL) { + console.log(`[DEBUG] GitHub repository URL provided: ${GITHUB_REPO_URL}`); + console.log(`[DEBUG] Setting up local GitHub repository...`); + await this.setupLocalGitHubRepo(); + console.log(`[DEBUG] GitHub repository setup completed.`); + } else { + console.log(`[DEBUG] No GitHub repository URL provided. Skipping GitHub setup.`); + } + + console.log(`[DEBUG] Setting up Gitea remote repository for project "${projectId}"...`); + const giteaRemoteUrl = await this.setupGiteaRemote(projectId); + console.log(`[DEBUG] Gitea remote URL: ${giteaRemoteUrl.replace(/\/\/.*?@/, '//***@')}`); + + if (!GITHUB_REPO_URL) { + console.log(`[DEBUG] Setting up local repository with Gitea remote...`); + await this.setupLocalRepo(giteaRemoteUrl); + console.log(`[DEBUG] Local repository setup with Gitea remote completed.`); + } else { + console.log(`[DEBUG] Adding Gitea as additional remote to existing GitHub repository...`); + await this._addGiteaRemote(giteaRemoteUrl); + console.log(`[DEBUG] Gitea remote added to GitHub repository.`); + } + + console.log(`[DEBUG] Repository initialization for project "${projectId}" completed successfully.`); + console.log(`[DEBUG] Repository configuration: GitHub: ${GITHUB_REPO_URL ? 'Yes' : 'No'}, Gitea: Yes`); + + return { message: `Repository ${projectId} is ready.` }; + } catch (error) { + console.error(`[ERROR] Repository initialization for project "${projectId}" failed: ${error?.message}`); + + throw new Error(`Error during repo initialization: ${error.message}`); + } finally { + VCS.isInitRepoRunning = false; + console.log(`[DEBUG] Repository initialization process for "${projectId}" finished.`); + } + } + + // Checks for the existence of the remote repo and creates it if it doesn't exist + static async setupGiteaRemote(projectId) { + console.log(`[DEBUG] Checking Gitea remote repository "${projectId}"...`); + let repoData = await this.checkRepoExists(projectId); + if (!repoData) { + console.log(`[DEBUG] Gitea remote repository "${projectId}" does not exist. Creating...`); + repoData = await this.createRemoteRepo(projectId); + console.log(`[DEBUG] Gitea remote repository created: ${JSON.stringify(repoData)}`); + } else { + console.log(`[DEBUG] Gitea remote repository "${projectId}" already exists.`); + } + // Return the URL with token authentication + return `https://${USERNAME}:${API_TOKEN}@${GITEA_DOMAIN}/${USERNAME}/${projectId}.git`; + } + + // Sets up the local repository: either fetches/reset if .git exists, + // initializes git in a non-empty directory, or clones the repository if empty. + static async setupLocalRepo(remoteUrl) { + const gitDir = path.join(ROOT_PATH, '.git'); + const localRepoExists = await this.exists(gitDir); + if (localRepoExists) { + await this.fetchAndResetRepo(); + } else { + const files = await fs.readdir(ROOT_PATH); + if (files.length > 0) { + await this.initializeGitRepo(remoteUrl); + } else { + console.log('[DEBUG] Local directory is empty. Cloning remote repository...'); + await exec(`git clone ${remoteUrl} .`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + } + } + + static async setupLocalGitHubRepo() { + try { + if (!GITHUB_REPO_URL) { + console.log('[DEBUG] GITHUB_REPO_URL is not set. Skipping GitHub repo setup.'); + return; + } + + const gitDir = path.join(ROOT_PATH, '.git'); + const repoExists = await this.exists(gitDir); + + if (repoExists) { + console.log('[DEBUG] Git repository already initialized. Fetching and resetting...'); + + await this._addGithubRemote(); + + console.log('[DEBUG] Fetching GitHub remote...'); + await exec(`git fetch github`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + try { + console.log('[DEBUG] Checking for remote branch "github/ai-dev"...'); + await exec(`git rev-parse --verify github/ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Remote branch "github/ai-dev" exists. Resetting local repository to github/ai-dev...'); + await exec(`git reset --hard github/ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Switching to branch "ai-dev"...'); + await exec(`git checkout -B ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } catch (e) { + console.log('[DEBUG] Remote branch "github/ai-dev" does NOT exist. Creating initial commit...'); + await this.commitInitialChanges(); + } + return; + } + + console.log('[DEBUG] Initializing git in existing directory...'); + // const gitignorePath = path.join(ROOT_PATH, '.gitignore'); + // const ignoreContent = `node_modules/\n*/node_modules/\n*/build/\n`; + // await fs.writeFile(gitignorePath, ignoreContent, 'utf8'); + + await exec(`git init`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Configuring git user...'); + await exec(`git config user.email "support@flatlogic.com"`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + await exec(`git config user.name "Flatlogic Bot"`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + await this._addGithubRemote(); + + console.log('[DEBUG] Fetching GitHub remote...'); + await exec(`git fetch github`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + try { + console.log('[DEBUG] Checking for remote branch "github/ai-dev"...'); + await exec(`git rev-parse --verify github/ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Remote branch "github/ai-dev" exists. Resetting local repository to github/ai-dev...'); + await exec(`git reset --hard github/ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Switching to branch "ai-dev"...'); + await exec(`git checkout -B ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } catch (e) { + console.log('[DEBUG] Remote branch "github/ai-dev" does NOT exist. Creating initial commit...'); + await this.commitInitialChanges(); + } + } catch (error) { + console.error(`[ERROR] Failed to setup local GitHub repo: ${error?.message}`); + throw error; + } + } + + // Check if a file/directory exists + static async exists(pathToCheck) { + try { + await fs.access(pathToCheck); + return true; + } catch { + return false; + } + } + + // If the local repository exists, fetches remote data and resets the repository state + static async fetchAndResetRepo() { + console.log('[DEBUG] Local repository exists. Fetching remotes...'); + + if (GITHUB_REPO_URL) { + await exec(`git fetch github`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + const branchReset = await this.tryResetToBranch('ai-dev', 'github'); + + if (branchReset) { + return; + } + } + + await exec(`git fetch gitea`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + const branchReset = await this.tryResetToBranch('ai-dev', 'gitea'); + + if (!branchReset) { + const masterReset = await this.tryResetToBranch('master', 'gitea'); + if (masterReset) { + console.log('[DEBUG] Creating and switching to branch "ai-dev"...'); + await exec(`git branch ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + await exec(`git checkout ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Pushing ai-dev branch to remotes...'); + await exec(`git push -u gitea ai-dev --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (GITHUB_REPO_URL) { + await exec(`git push -u github ai-dev --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + } else { + console.log('[DEBUG] Neither "gitea/ai-dev" nor "gitea/master" exist. Creating initial commit...'); + await this.commitInitialChanges(); + } + } + } + + // Tries to check out and reset to the specified branch + static async tryResetToBranch(branchName, remote) { + try { + console.log(`[DEBUG] Checking for remote branch "${remote}/${branchName}"...`); + await exec(`git rev-parse --verify ${remote}/${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Remote branch "${remote}/${branchName}" found. Resetting local repository to "${remote}/${branchName}"...`); + await exec(`git reset --hard ${remote}/${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + await exec(`git checkout ${branchName === 'ai-dev' ? 'ai-dev' : branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + return true; + } catch (e) { + console.log(`[DEBUG] Remote branch "${remote}/${branchName}" does NOT exist.`); + + return false; + } + } + + // If remote branch doesn't exist, make the initial commit and set up branches + static async commitInitialChanges() { + console.log('[DEBUG] Adding all files for initial commit...'); + await exec(`git add .`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + const { stdout: status } = await exec(`git status --porcelain`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (status.trim()) { + await exec(`git commit -m "Initial version"`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + if (GITHUB_REPO_URL) { + await exec(`git push -u github master --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + await exec(`git push -u gitea master --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + console.log('[DEBUG] Creating and switching to branch "ai-dev"...'); + await exec(`git branch ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + await exec(`git checkout ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Making ai-dev branch identical to master...'); + + if (GITHUB_REPO_URL) { + await exec(`git reset --hard github/master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } else { + await exec(`git reset --hard gitea/master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + + console.log('[DEBUG] Pushing ai-dev branch to remotes...'); + if (GITHUB_REPO_URL) { + await exec(`git push -u github ai-dev --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + await exec(`git push -u gitea ai-dev --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } else { + console.log('[DEBUG] No local changes to commit.'); + } + } + + // If the local directory is not empty but .git doesn't exist, initialize git, + // add .gitignore, configure the user, and add the remote origin. + static async initializeGitRepo(giteaRemoteUrl) { + console.log('[DEBUG] Local directory is not empty. Initializing git...'); + const gitignorePath = path.join(ROOT_PATH, '.gitignore'); + const ignoreContent = `node_modules/\n*/node_modules/\n*/build/\n`; + await fs.writeFile(gitignorePath, ignoreContent, 'utf8'); + + await exec(`git init`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Configuring git user...'); + await exec(`git config user.email "support@flatlogic.com"`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + await exec(`git config user.name "Flatlogic Bot"`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + console.log(`[DEBUG] Adding Gitea remote ${giteaRemoteUrl}...`); + await exec(`git remote add gitea ${giteaRemoteUrl}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + if (GITHUB_REPO_URL) { + await this._addGithubRemote(); + } + + console.log('[DEBUG] Fetching Gitea remote...'); + await exec(`git fetch gitea`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + try { + console.log('[DEBUG] Checking for remote branch "gitea/ai-dev"...'); + await exec(`git rev-parse --verify gitea/ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Remote branch "gitea/ai-dev" exists. Resetting local repository to gitea/ai-dev...'); + await exec(`git reset --hard gitea/ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Switching to branch "ai-dev"...'); + await exec(`git checkout -B ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } catch (e) { + console.log('[DEBUG] Remote branch "gitea/ai-dev" does NOT exist. Creating initial commit...'); + await this.commitInitialChanges(); + } + } + + // Method to check if the repository exists on remote server + static async checkRepoExists(repoName) { + const url = `https://${GITEA_DOMAIN}/api/v1/repos/${USERNAME}/${repoName}`; + try { + const response = await axios.get(url, { + headers: { Authorization: `token ${API_TOKEN}` } + }); + return response.data; + } catch (err) { + if (err.response && err.response.status === 404) { + return null; + } + throw new Error('Error checking repository existence: ' + err?.message); + } + } + + // Method to create a remote repository via API + static async createRemoteRepo(repoName) { + const createUrl = `https://${GITEA_DOMAIN}/api/v1/user/repos`; + console.log("[DEBUG] createUrl", createUrl); + + try { + const response = await axios.post(createUrl, { + name: repoName, + description: `Repository for project ${repoName}`, + private: false + }, { + headers: { Authorization: `token ${API_TOKEN}` } + }); + + return response.data; + } catch (err) { + console.log('Error creating repository via API: ' + err?.message) + // throw new Error('Error creating repository via API: ' + err.message); + } + } + + static async commitChanges(message = "", files = '.', dev_schema) { + try { + console.log(`[DEBUG] Starting commit process...`); + await this._ensureDevBranch(); + + console.log(`[DEBUG] Ensuring .gitignore is properly configured...`); + await this._ensureGitignore(); + + // Save dev_schema + await this._saveDevSchema(message, dev_schema); + + console.log(`[DEBUG] Adding files to git index: ${files}`); + if (files === '.') { + await exec(`git add .`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } else { + await exec(`git add ${files}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + + const { stdout: status } = await exec('git status --porcelain', { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Git status before commit: ${status}`); + + if (!status.trim()) { + console.log(`[DEBUG] No changes to commit`); + return { message: "No changes to commit" }; + } + + const now = new Date(); + const commitMessage = message || `Auto commit: ${now.toISOString()}`; + console.log(`[DEBUG] Committing changes with message: "${commitMessage}"`); + + const { stdout: commitOutput, stderr: commitError } = await exec(`git commit -m "${commitMessage}"`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Commit output: ${commitOutput}`); + if (commitError) { + console.log(`[DEBUG] Commit stderr: ${commitError}`); + } + + const { stdout: currentBranch } = await exec(`git rev-parse --abbrev-ref HEAD`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + const branchName = currentBranch.trim(); + console.log(`[DEBUG] Current branch: ${branchName}`); + + console.log(`[DEBUG] Pushing changes to Gitea...`); + try { + const { stdout: giteaPushOutput, stderr: giteaPushError } = await exec(`git push gitea ${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Gitea push output: ${giteaPushOutput}`); + if (giteaPushError) { + console.log(`[DEBUG] Gitea push stderr: ${giteaPushError}`); + } + } catch (giteaError) { + console.error(`[ERROR] Failed to push to Gitea: ${giteaError?.message}`); + + if (giteaError.stderr && giteaError.stderr.includes('rejected')) { + console.log(`[DEBUG] Push rejected, trying with --force...`); + try { + const { stdout, stderr } = await exec(`git push gitea ${branchName} --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Force push to Gitea output: ${stdout}`); + if (stderr) { + console.log(`[DEBUG] Force push to Gitea stderr: ${stderr}`); + } + } catch (forceError) { + console.error(`[ERROR] Force push to Gitea failed: ${forceError?.message}`); + } + } + } + + if (GITHUB_REPO_URL) { + console.log(`[DEBUG] Pushing changes to GitHub...`); + try { + const { stdout: githubPushOutput, stderr: githubPushError } = await exec(`git push github ${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] GitHub push output: ${githubPushOutput}`); + if (githubPushError) { + console.log(`[DEBUG] GitHub push stderr: ${githubPushError}`); + } + } catch (githubError) { + console.error(`[ERROR] Failed to push to GitHub: ${githubError?.message}`); + + if (githubError.stderr && githubError.stderr.includes('rejected')) { + console.log(`[DEBUG] Push rejected, trying with --force...`); + try { + const { stdout, stderr } = await exec(`git push github ${branchName} --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Force push to GitHub output: ${stdout}`); + if (stderr) { + console.log(`[DEBUG] Force push to GitHub stderr: ${stderr}`); + } + } catch (forceError) { + console.error(`[ERROR] Force push to GitHub failed: ${forceError?.message}`); + } + } + } + } + + console.log(`[DEBUG] Commit process completed`); + return { message: "Changes committed" }; + } catch (error) { + console.error(`[ERROR] Error during commit process: ${error?.message}`); + } + } + + static async getLog() { + try { + const remote = GITHUB_REPO_URL ? 'github' : 'gitea'; + + const { stdout: remotes } = await exec(`git remote -v`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Remotes: ${remotes}`); + + const { stdout: branches } = await exec(`git branch -a`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Branches: ${branches}`); + + const { stdout } = await exec(`git log ${remote}/ai-dev --oneline`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + const lines = stdout.split(/\r?\n/).filter(line => line.trim() !== ''); + const result = {}; + lines.forEach((line) => { + const firstSpaceIndex = line.indexOf(' '); + if (firstSpaceIndex > 0) { + const hash = line.substring(0, firstSpaceIndex); + const message = line.substring(firstSpaceIndex + 1).trim(); + result[hash] = message; + } + }); + return result; + } catch (error) { + console.error(`[ERROR] Error during get log: ${error?.message}`); + throw error; + } + } + + static async checkout(ref) { + try { + await exec(`git checkout ${ref}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + return { message: `Checked out to ${ref}` }; + } catch (error) { + throw new Error(`Error during checkout: ${error?.message}`); + } + } + + static async revert(commitHash) { + try { + const { stdout: currentBranch } = await exec(`git rev-parse --abbrev-ref HEAD`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + const branchName = currentBranch.trim(); + + await exec(`git reset --hard`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + await exec( + `git revert --no-edit ${commitHash}..HEAD --no-commit`, + { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER } + ); + + await exec( + `git commit -m "Revert to version ${commitHash}"`, + { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER } + ); + + await exec(`git push gitea ${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (GITHUB_REPO_URL) { + await exec(`git push github ${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + + const commitMessage = await this._getCommitMessageByHash(commitHash); + const devSchema = await this._getDevSchemaByCommitMessage(commitMessage); + + return { message: `Reverted to commit ${commitHash}`, devSchema }; + } catch (error) { + console.error("Error during revert:", error?.message); + if (error.stdout) { + console.error("Revert stdout:", error.stdout); + } + if (error.stderr) { + console.error("Revert stderr:", error.stderr); + } + throw new Error(`Error during revert: ${error?.message}`); + } + } + + static async mergeDevIntoMaster() { + try { + // First, make sure we have the latest changes from both branches + console.log('[DEBUG] Fetching latest changes from remote repositories...'); + await exec(`git fetch gitea`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (GITHUB_REPO_URL) { + await exec(`git fetch github`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + + // Switch to branch 'master' + console.log('[DEBUG] Switching to branch "master"...'); + await exec(`git checkout master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + // Pull latest changes from master + console.log('[DEBUG] Pulling latest changes from master branch...'); + try { + await exec(`git pull gitea master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Successfully pulled from Gitea master'); + } catch (pullError) { + console.warn(`[WARN] Failed to pull from Gitea master: ${pullError?.message}`); + // Try to continue anyway + } + + // Switch to ai-dev and make sure it's up to date + console.log('[DEBUG] Switching to branch "ai-dev"...'); + await exec(`git checkout ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + // Pull latest changes from ai-dev + console.log('[DEBUG] Pulling latest changes from ai-dev branch...'); + try { + await exec(`git pull gitea ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log('[DEBUG] Successfully pulled from Gitea ai-dev'); + } catch (pullError) { + console.warn(`[WARN] Failed to pull from Gitea ai-dev: ${pullError?.message}`); + // Try to continue anyway + } + + // Switch back to master for the merge + console.log('[DEBUG] Switching back to branch "master"...'); + await exec(`git checkout master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + // Merge branch 'ai-dev' into 'master' with a forced merge. + // Parameter -X theirs is used to resolve conflicts by keeping the changes from the branch being merged in case of conflicts. + console.log('[DEBUG] Merging branch "ai-dev" into "master" (force merge with -X theirs)...'); + try { + const { stdout: mergeOutput, stderr: mergeError } = await exec( + `git merge ai-dev --no-ff -X theirs -m "Forced merge: merge ai-dev into master"`, + { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER } + ); + console.log(`[DEBUG] Merge output: ${mergeOutput}`); + if (mergeError) { + console.log(`[DEBUG] Merge stderr: ${mergeError}`); + } + } catch (mergeError) { + console.error(`[ERROR] Merge failed: ${mergeError?.message}`); + if (mergeError.stdout) { + console.error(`[ERROR] Merge stdout: ${mergeError.stdout}`); + } + if (mergeError.stderr) { + console.error(`[ERROR] Merge stderr: ${mergeError.stderr}`); + } + + // Abort the merge if it failed + console.log('[DEBUG] Aborting failed merge...'); + await exec(`git merge --abort`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + throw new Error(`Failed to merge ai-dev into master: ${mergeError?.message}`); + } + + // Push the merged 'master' branch to both remotes + console.log('[DEBUG] Pushing merged master branch to Gitea remote...'); + try { + const { stdout: giteaPushOutput, stderr: giteaPushError } = await exec(`git push gitea master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Gitea push output: ${giteaPushOutput}`); + if (giteaPushError) { + console.log(`[DEBUG] Gitea push stderr: ${giteaPushError}`); + } + } catch (pushError) { + console.error(`[ERROR] Failed to push to Gitea: ${pushError?.message}`); + + // If push is rejected, try with --force + if (pushError.stderr && pushError.stderr.includes('rejected')) { + console.log('[DEBUG] Push rejected, trying with --force...'); + try { + const { stdout, stderr } = await exec(`git push gitea master --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Force push to Gitea output: ${stdout}`); + if (stderr) { + console.log(`[DEBUG] Force push to Gitea stderr: ${stderr}`); + } + } catch (forceError) { + console.error(`[ERROR] Force push to Gitea also failed: ${forceError?.message}`); + throw forceError; + } + } else { + throw pushError; + } + } + + if (GITHUB_REPO_URL) { + console.log('[DEBUG] Pushing merged master branch to GitHub remote...'); + try { + const { stdout: githubPushOutput, stderr: githubPushError } = await exec(`git push github master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] GitHub push output: ${githubPushOutput}`); + if (githubPushError) { + console.log(`[DEBUG] GitHub push stderr: ${githubPushError}`); + } + } catch (pushError) { + console.error(`[ERROR] Failed to push to GitHub: ${pushError?.message}`); + + // If push is rejected, try with --force + if (pushError.stderr && pushError.stderr.includes('rejected')) { + console.log('[DEBUG] Push rejected, trying with --force...'); + try { + const { stdout, stderr } = await exec(`git push github master --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Force push to GitHub output: ${stdout}`); + if (stderr) { + console.log(`[DEBUG] Force push to GitHub stderr: ${stderr}`); + } + } catch (forceError) { + console.error(`[ERROR] Force push to GitHub also failed: ${forceError?.message}`); + throw forceError; + } + } else { + throw pushError; + } + } + } + + return { message: "Branch ai-dev merged into master and pushed to all remotes" }; + } catch (error) { + console.error(`[ERROR] Error during mergeDevIntoMaster: ${error?.message}`); + throw new Error(`Error during merge of ai-dev into master: ${error?.message}`); + } + } + + static async _mergeDevIntoMasterGitHub() { + try { + // Switch to branch 'master' + console.log('Switching to branch "master" (GitHub)...'); + await exec(`git checkout master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + // Merge branch 'ai-dev' into 'master' with a forced merge. + console.log('Merging branch "ai-dev" into "master" (GitHub, force merge with -X theirs)...'); + await exec( + `git merge ai-dev --no-ff -X theirs -m "Forced merge: merge ai-dev into master"`, + { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER } + ); + + // Push the merged 'master' branch to remote (GitHub) + console.log('Pushing merged master branch to remote (GitHub)...'); + const { stdout, stderr } = await exec(`git push -f github master`, { + cwd: ROOT_PATH, + maxBuffer: MAX_BUFFER + }); + if (stdout) { + console.log("Git push GitHub stdout:", stdout); + } + if (stderr) { + console.error("Git push GitHub stderr:", stderr); + } + return { message: "Branch ai-dev merged into master and pushed to GitHub remote" }; + } catch (error) { + console.error("Error during mergeDevIntoMasterGitHub:", error?.message); + if (error.stdout) { + console.error("Merge GitHub stdout:", error.stdout); + } + if (error.stderr) { + console.error("Merge GitHub stderr:", error.stderr); + } + throw error; + } + } + + static async resetDevBranch() { + try { + console.log(`[DEBUG] Starting reset of ai-dev branch to match master...`); + + // First, fetch all remote branches to ensure we have the latest information + console.log(`[DEBUG] Fetching latest changes from remotes...`); + await exec(`git fetch --all`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + // Check current branch state + const { stdout: initialBranches } = await exec(`git branch -a`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Initial branches: ${initialBranches}`); + + // Check if master branch exists + const { stdout: masterExists } = await exec(`git branch --list master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (!masterExists.trim()) { + console.log(`[DEBUG] Master branch does not exist. Creating it...`); + await exec(`git checkout -b master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } + + // Switch to master branch + console.log(`[DEBUG] Switching to branch "master"...`); + await exec(`git checkout master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + // Pull latest changes from master + console.log(`[DEBUG] Pulling latest changes from master...`); + try { + await exec(`git pull gitea master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } catch (error) { + console.log(`[DEBUG] Error pulling from master: ${error?.message}`); + } + + // Verify we are on master branch + const { stdout: currentBranch } = await exec(`git rev-parse --abbrev-ref HEAD`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Current branch after checkout: ${currentBranch.trim()}`); + + // Get master branch commit hash + const { stdout: masterCommit } = await exec(`git rev-parse master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Master branch commit hash: ${masterCommit.trim()}`); + + // Delete local ai-dev branch if it exists + try { + await exec(`git branch -D ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Local branch ai-dev deleted successfully`); + } catch (error) { + console.log(`[DEBUG] Local branch ai-dev does not exist or could not be deleted: ${error?.message}`); + } + + // Create new ai-dev branch from master using the exact commit hash + await exec(`git branch ai-dev ${masterCommit.trim()}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Created new ai-dev branch from master commit ${masterCommit.trim()}`); + + // Switch to the new ai-dev branch + await exec(`git checkout ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Switched to new ai-dev branch`); + + // Verify we are on ai-dev branch + const { stdout: newCurrentBranch } = await exec(`git rev-parse --abbrev-ref HEAD`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Current branch after creating ai-dev: ${newCurrentBranch.trim()}`); + + // Verify that ai-dev points to the same commit as master + const { stdout: aiDevCommit } = await exec(`git rev-parse ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] ai-dev branch commit hash: ${aiDevCommit.trim()}`); + + if (aiDevCommit.trim() !== masterCommit.trim()) { + console.error(`[ERROR] ai-dev branch does not point to the same commit as master!`); + console.error(`[ERROR] master: ${masterCommit.trim()}, ai-dev: ${aiDevCommit.trim()}`); + throw new Error(`Failed to create ai-dev branch from master`); + } + + console.log(`[DEBUG] Verified: ai-dev branch points to the same commit as master`); + + // Delete remote ai-dev branches if they exist + console.log(`[DEBUG] Deleting remote ai-dev branches if they exist...`); + + // For Gitea + try { + // First check if the remote branch exists + const { stdout: giteaBranches } = await exec(`git ls-remote --heads gitea ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + if (giteaBranches.trim()) { + console.log(`[DEBUG] Remote branch ai-dev exists on Gitea, deleting it...`); + await exec(`git push gitea --delete ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Remote branch ai-dev on Gitea deleted successfully`); + + // Verify deletion + const { stdout: verifyGiteaDeletion } = await exec(`git ls-remote --heads gitea ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (verifyGiteaDeletion.trim()) { + console.log(`[WARN] Remote branch ai-dev on Gitea still exists after deletion attempt`); + } else { + console.log(`[DEBUG] Verified: Remote branch ai-dev on Gitea is deleted`); + } + } else { + console.log(`[DEBUG] Remote branch ai-dev does not exist on Gitea`); + } + } catch (error) { + console.log(`[DEBUG] Error checking/deleting remote branch on Gitea: ${error?.message}`); + } + + // For GitHub + if (GITHUB_REPO_URL) { + try { + // First check if the remote branch exists + const { stdout: githubBranches } = await exec(`git ls-remote --heads github ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + if (githubBranches.trim()) { + console.log(`[DEBUG] Remote branch ai-dev exists on GitHub, deleting it...`); + await exec(`git push github --delete ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Remote branch ai-dev on GitHub deleted successfully`); + + // Verify deletion + const { stdout: verifyGithubDeletion } = await exec(`git ls-remote --heads github ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (verifyGithubDeletion.trim()) { + console.log(`[WARN] Remote branch ai-dev on GitHub still exists after deletion attempt`); + } else { + console.log(`[DEBUG] Verified: Remote branch ai-dev on GitHub is deleted`); + } + } else { + console.log(`[DEBUG] Remote branch ai-dev does not exist on GitHub`); + } + } catch (error) { + console.log(`[DEBUG] Error checking/deleting remote branch on GitHub: ${error?.message}`); + } + } + + // Wait a moment to ensure deletion is processed + console.log(`[DEBUG] Waiting for remote branch deletion to be processed...`); + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Push new ai-dev branch to remote repositories with force + console.log(`[DEBUG] Pushing new ai-dev branch to Gitea (force push)...`); + try { + const { stdout, stderr } = await exec(`git push -u gitea ai-dev --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Gitea force push output: ${stdout}`); + if (stderr) { + console.log(`[DEBUG] Gitea force push stderr: ${stderr}`); + } + + // Verify the push + const { stdout: verifyGiteaPush } = await exec(`git ls-remote --heads gitea ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Gitea ai-dev branch after push: ${verifyGiteaPush}`); + + // Extract the hash from the output + const giteaAiDevHash = verifyGiteaPush.split(/\s+/)[0]; + + if (giteaAiDevHash === masterCommit.trim()) { + console.log(`[DEBUG] Verified: Gitea ai-dev branch matches master branch`); + } else { + console.log(`[WARN] Gitea ai-dev branch does not match master branch!`); + console.log(`[WARN] master: ${masterCommit.trim()}, Gitea ai-dev: ${giteaAiDevHash}`); + } + } catch (error) { + console.error(`[ERROR] Force push to Gitea failed: ${error?.message}`); + throw error; + } + + if (GITHUB_REPO_URL) { + console.log(`[DEBUG] Pushing new ai-dev branch to GitHub (force push)...`); + try { + const { stdout, stderr } = await exec(`git push -u github ai-dev --force`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] GitHub force push output: ${stdout}`); + if (stderr) { + console.log(`[DEBUG] GitHub force push stderr: ${stderr}`); + } + + // Verify the push + const { stdout: verifyGithubPush } = await exec(`git ls-remote --heads github ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] GitHub ai-dev branch after push: ${verifyGithubPush}`); + + // Extract the hash from the output + const githubAiDevHash = verifyGithubPush.split(/\s+/)[0]; + + if (githubAiDevHash === masterCommit.trim()) { + console.log(`[DEBUG] Verified: GitHub ai-dev branch matches master branch`); + } else { + console.log(`[WARN] GitHub ai-dev branch does not match master branch!`); + console.log(`[WARN] master: ${masterCommit.trim()}, GitHub ai-dev: ${githubAiDevHash}`); + } + } catch (error) { + console.error(`[ERROR] Force push to GitHub failed: ${error?.message}`); + throw error; + } + } + + // Final verification + console.log(`[DEBUG] Performing final verification...`); + + // Get master commit hash again to ensure it hasn't changed + const { stdout: finalMasterCommit } = await exec(`git rev-parse master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Final master branch commit hash: ${finalMasterCommit.trim()}`); + + // Get ai-dev commit hash + const { stdout: finalAiDevCommit } = await exec(`git rev-parse ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Final ai-dev branch commit hash: ${finalAiDevCommit.trim()}`); + + // Get remote branches + const { stdout: finalRemoteBranches } = await exec(`git ls-remote --heads`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Final remote branches: ${finalRemoteBranches}`); + + if (finalAiDevCommit.trim() !== finalMasterCommit.trim()) { + console.error(`[ERROR] Final verification failed: ai-dev and master branches point to different commits!`); + console.error(`[ERROR] master: ${finalMasterCommit.trim()}, ai-dev: ${finalAiDevCommit.trim()}`); + } else { + console.log(`[DEBUG] Final verification passed: ai-dev and master branches point to the same commit`); + } + + console.log(`[DEBUG] Reset of ai-dev branch completed successfully`); + return { message: "Branch ai-dev has been reset to be an exact copy of master" }; + } catch (error) { + console.error(`[ERROR] Error during reset of dev branch: ${error?.message}`); + throw new Error(`Error during reset of dev branch: ${error?.message}`); + } + } + + static async _resetDevBranchGitHub() { + try { + console.log('[DEBUG] Switching to branch "master" (GitHub)...'); + await exec(`git checkout master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + console.log('[DEBUG] Resetting branch "ai-dev" to be identical to "master" (GitHub)...'); + await exec(`git checkout -B ai-dev master`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + console.log('[DEBUG] Pushing updated branch "ai-dev" to remote (GitHub, force push)...'); + await exec(`git push -f github ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + + return { message: 'ai-dev branch successfully reset to master (GitHub).' }; + } catch (error) { + console.error("Error during resetting ai-dev branch (GitHub):", error?.message); + if (error.stdout) { + console.error("Reset GitHub stdout:", error.stdout); + } + if (error.stderr) { + console.error("Reset GitHub stderr:", error.stderr); + } + throw new Error(`Error during resetting ai-dev branch (GitHub): ${error?.message}`); + } + } + + static async _pushChangesToGitea() { + try { + const { stdout, stderr } = await exec(`git push gitea ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (stdout) { + console.log("Git push Gitea stdout:", stdout); + } + if (stderr) { + console.error("Git push Gitea stderr:", stderr); + } + return { message: "Changes pushed to Gitea remote repository (ai-dev branch)" }; + } catch (error) { + console.error("Git push Gitea error:", error?.message); + if (error.stdout) { + console.error("Git push Gitea stdout:", error.stdout); + } + if (error.stderr) { + console.error("Git push Gitea stderr:", error.stderr); + } + throw error; + } + } + + static async _pushChangesToGithub() { + try { + const { stdout, stderr } = await exec(`git push github ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (stdout) { + console.log("Git push GitHub stdout:", stdout); + } + if (stderr) { + console.error("Git push GitHub stderr:", stderr); + } + return { message: "Changes pushed to GitHub repository (ai-dev branch)" }; + } catch (error) { + console.error("Git push GitHub error:", error?.message); + if (error.stdout) { + console.error("Git push GitHub stdout:", error.stdout); + } + if (error.stderr) { + console.error("Git push GitHub stderr:", error.stderr); + } + throw error; + } + } + + static async _addGithubRemote() { + if (GITHUB_REPO_URL) { + try { + const { stdout: remotes } = await exec(`git remote -v`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (!remotes.includes('github')) { + console.log(`[DEBUG] Adding GitHub remote: git remote add github ${GITHUB_REPO_URL}`); + await exec(`git remote add github ${GITHUB_REPO_URL}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] GitHub remote added: ${GITHUB_REPO_URL}`); + } else { + console.log(`[DEBUG] GitHub remote already exists.`); + } + } catch (error) { + console.error(`[ERROR] Failed to add GitHub remote: ${error?.message}`); + if (error.stdout) { + console.error(`[ERROR] git remote add stdout: ${error.stdout}`); + } + if (error.stderr) { + console.error(`[ERROR] git remote add stderr: ${error.stderr}`); + } + throw error; + } + } + } + + static async _addGiteaRemote(giteaRemoteUrl) { + try { + const { stdout: remotes } = await exec(`git remote -v`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + if (!remotes.includes('gitea')) { + console.log(`[DEBUG] Adding Gitea remote: git remote add gitea ${giteaRemoteUrl}`); + await exec(`git remote add gitea ${giteaRemoteUrl}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + console.log(`[DEBUG] Gitea remote added: ${giteaRemoteUrl}`); + } else { + console.log(`[DEBUG] Gitea remote already exists.`); + } + } catch (error) { + console.error(`[ERROR] Failed to add Gitea remote: ${error?.message}`); + if (error.stdout) { + console.error(`[ERROR] git remote add stdout: ${error.stdout}`); + } + if (error.stderr) { + console.error(`[ERROR] git remote add stderr: ${error.stderr}`); + } + throw error; + } + } + + static async _revertGitHubChanges(branchName) { + try { + await exec(`git push -f github ${branchName}`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } catch (error) { + console.error("Error during revertGitHubChanges:", error?.message); + if (error.stdout) { + console.error("revertGitHubChanges stdout:", error.stdout); + } + if (error.stderr) { + console.error("revertGitHubChanges stderr:", error.stderr); + } + throw new Error(`Error during revertGitHubChanges: ${error?.message}`); + } + } + + static async _ensureDevBranch() { + try { + console.log(`[DEBUG] Ensuring we are on 'ai-dev' branch...`); + + const { stdout: branchList } = await exec(`git branch --list ai-dev`, { + cwd: ROOT_PATH, + maxBuffer: MAX_BUFFER, + }); + + if (!branchList || branchList.trim() === '') { + console.log(`[DEBUG] Branch 'ai-dev' not found. Creating branch 'ai-dev'.`); + await exec(`git checkout -b ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } else { + const { stdout: currentBranchStdout } = await exec(`git rev-parse --abbrev-ref HEAD`, { + cwd: ROOT_PATH, + maxBuffer: MAX_BUFFER, + }); + const currentBranch = currentBranchStdout.trim(); + + if (currentBranch !== 'ai-dev') { + console.log(`[DEBUG] Switching from branch '${currentBranch}' to 'ai-dev'.`); + await exec(`git checkout ai-dev`, { cwd: ROOT_PATH, maxBuffer: MAX_BUFFER }); + } else { + console.log(`[DEBUG] Already on branch 'ai-dev'.`); + } + } + + console.log(`[DEBUG] Successfully ensured we are on 'ai-dev' branch.`); + } catch (error) { + console.error(`[ERROR] Error ensuring branch 'ai-dev': ${error?.message}`); + if (error.stdout) { + console.error(`[ERROR] stdout: ${error.stdout}`); + } + if (error.stderr) { + console.error(`[ERROR] stderr: ${error.stderr}`); + } + throw new Error(`Error ensuring branch 'ai-dev': ${error?.message}`); + } + } + + static async _ensureGitignore() { + try { + console.log(`[DEBUG] Checking .gitignore file...`); + const gitignorePath = path.join(ROOT_PATH, '.gitignore'); + + let gitignoreContent = ''; + try { + gitignoreContent = await fs.readFile(gitignorePath, 'utf8'); + console.log(`[DEBUG] Existing .gitignore found.`); + } catch (error) { + console.log(`[DEBUG] .gitignore file not found, creating new one.`); + } + + + const requiredPatterns = [ + 'node_modules/', + '*/node_modules/', + '**/node_modules/', + '*/build/', + '**/build/', + '.DS_Store', + '.env' + ]; + + let needsUpdate = false; + for (const pattern of requiredPatterns) { + if (!gitignoreContent.includes(pattern)) { + gitignoreContent += `\n${pattern}`; + needsUpdate = true; + } + } + + if (needsUpdate) { + console.log(`[DEBUG] Updating .gitignore file with missing patterns.`); + await fs.writeFile(gitignorePath, gitignoreContent.trim(), 'utf8'); + console.log(`[DEBUG] .gitignore file updated successfully.`); + } else { + console.log(`[DEBUG] .gitignore file is up to date.`); + } + + return true; + } catch (error) { + console.error(`[ERROR] Error ensuring .gitignore: ${error?.message}`); + return false; + } + } + + static async _waitForGitLockRelease(gitDir, timeout = 10000, interval = 500) { + const lockFilePath = path.join(gitDir, 'index.lock'); + const startTime = Date.now(); + + while (Date.now() - startTime < timeout) { + try { + await fs.access(lockFilePath); + console.log('[DEBUG] index.lock file exists. Waiting...'); + await new Promise(resolve => setTimeout(resolve, interval)); + } catch (err) { + console.log('[DEBUG] index.lock file no longer exists. Proceeding...'); + return; + } + } + + throw new Error('Timeout waiting for index.lock to be released'); + } + + static async _removeGitLockIfExists(gitDir) { + const lockFilePath = path.join(gitDir, 'index.lock'); + try { + await fs.access(lockFilePath); + console.log('[DEBUG] index.lock file exists. Removing...'); + await fs.unlink(lockFilePath); + console.log('[DEBUG] index.lock file removed.'); + } catch (err) { + console.log('[DEBUG] index.lock file does not exist. No action needed.'); + } + } + + static async _saveDevSchema(commitMessage, dev_schema) { + try { + let devSchemaData = {}; + try { + const fileContent = await fs.readFile(devSchemaFilePath, 'utf8'); + devSchemaData = JSON.parse(fileContent); + } catch (readError) { + console.log(`[DEBUG] _dev_schema.json not found or empty, creating new.`); + devSchemaData = {}; + } + + const schema = JSON.parse(dev_schema); + + devSchemaData[commitMessage] = JSON.stringify(schema); + + await fs.writeFile(devSchemaFilePath, JSON.stringify(devSchemaData, null, 2), 'utf8'); + console.log(`[DEBUG] _dev_schema.json updated with new schema for commit '${commitMessage}'`); + } catch (error) { + console.error(`[ERROR] Error saving dev schema: ${error?.message}`); + throw new Error(`Error saving dev schema: ${error?.message}`); + } + } + + static async _getDevSchemaByHash(hash) { + try { + const fileContent = await fs.readFile(devSchemaFilePath, 'utf8'); + const devSchemaData = JSON.parse(fileContent); + + if (devSchemaData[hash]) { + return devSchemaData[hash]; + } else { + throw new Error(`Schema not found for commit hash: ${hash}`); + } + } catch (error) { + console.error(`[ERROR] Error reading dev schema: ${error?.message}`); + console.error(`Error reading dev schema: ${error?.message}`); + } + } + + static async _getDevSchemaByCommitMessage(commitMessage) { + try { + const fileContent = await fs.readFile(devSchemaFilePath, 'utf8'); + const devSchemaData = JSON.parse(fileContent); + + if (devSchemaData[commitMessage]) { + return devSchemaData[commitMessage]; + } else { + throw new Error(`Schema not found for commit message: ${commitMessage}`); + } + } catch (error) { + console.error(`[ERROR] Error retrieving dev schema: ${error.message}`); + throw new Error(`Error retrieving dev schema: ${error.message}`); + } + } + + static async _getCommitMessageByHash(commitHash) { + return new Promise((resolve, reject) => { + exec(`git log -1 --format=%B ${commitHash}`, (error, stdout, stderr) => { + if (error) { + reject(`Error getting commit message: ${stderr}`); + } else { + resolve(stdout.trim()); + } + }); + }); + } +} + +module.exports = VCS; \ No newline at end of file diff --git a/app-shell/yarn.lock b/app-shell/yarn.lock new file mode 100644 index 0000000..63ccb71 --- /dev/null +++ b/app-shell/yarn.lock @@ -0,0 +1,3044 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + 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" + +"@one-ini/wasm@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" + integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + +accepts@^1.3.7, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.1, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array.prototype.map@^1.0.1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.7.tgz#82fa4d6027272d1fca28a63bbda424d0185d78a7" + integrity sha512-XpcFfLoBEAhezrrNw1V+yLXkE7M6uR7xJEsxbG6c/V9v043qurwVJB9r9UTnoSioFDoz1i1VOydpWGmJpfVZbg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-array-method-boxes-properly "^1.0.0" + es-object-atoms "^1.0.0" + is-string "^1.0.7" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axios@^1.6.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64url@3.x.x: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +bcrypt@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2" + integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.11" + node-addon-api "^5.0.0" + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + 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.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + integrity sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg== + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + 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" + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^3.5.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + 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" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +cli-color@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.4.tgz#d658080290968816b322248b7306fad2346fb2c8" + integrity sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA== + dependencies: + d "^1.0.1" + es5-ext "^0.10.64" + es6-iterator "^2.0.3" + memoizee "^0.4.15" + timers-ext "^0.1.7" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-env@7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^7.0.0, cross-spawn@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +csv-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-3.0.0.tgz#b88a6256d79e090a97a1b56451f9327b01d710e7" + integrity sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ== + dependencies: + minimist "^1.2.0" + +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +debug@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-libc@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + integrity sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg== + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + +diff@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +editorconfig@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-1.0.4.tgz#040c9a8e9a6c5288388b87c2db07028aa89f53a3" + integrity sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q== + dependencies: + "@one-ini/wasm" "0.1.1" + commander "^10.0.0" + minimatch "9.0.1" + semver "^7.5.3" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +es-abstract@^1.17.0-next.1, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.0.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.2: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +express@4.18.2: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== + dependencies: + is-buffer "~2.0.3" + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formidable@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" + integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2, fresh@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + 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" + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + 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" + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +glob-parent@~5.1.0, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^10.3.3: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + 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" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.0, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +helmet@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-4.1.1.tgz#751f0e273d809ace9c172073e0003bed27d27a4a" + integrity sha512-Avg4XxSBrehD94mkRwEljnO+6RZx7AGfk8Wa6K1nxaU+hbXlFOhlOIMgPfFqOYQB/dBCsTpootTGuiOG+CHiQA== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.4, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +iterate-iterator@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" + integrity sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw== + +iterate-value@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +js-beautify@^1.14.5: + version "1.15.1" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.1.tgz#4695afb508c324e1084ee0b952a102023fc65b64" + integrity sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA== + dependencies: + config-chain "^1.1.13" + editorconfig "^1.0.4" + glob "^10.3.3" + js-cookie "^3.0.5" + nopt "^7.2.0" + +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json2csv@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/json2csv/-/json2csv-5.0.7.tgz#f3a583c25abd9804be873e495d1e65ad8d1b54ae" + integrity sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA== + dependencies: + commander "^6.1.0" + jsonparse "^1.3.1" + lodash.get "^4.4.2" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsonwebtoken@8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + 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 "^5.6.0" + +jsonwebtoken@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + 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" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lodash@4.17.21, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== + dependencies: + es5-ext "~0.10.2" + +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memoizee@^0.4.15: + version "0.4.17" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.17.tgz#942a5f8acee281fa6fb9c620bddc57e3b7382949" + integrity sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA== + dependencies: + d "^1.0.2" + es5-ext "^0.10.64" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-descriptors@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +methods@^1.1.2, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0, mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" + integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mocha@8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.3.tgz#5e93f873e35dfdd69617ea75f9c68c2ca61c2ac5" + integrity sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.4.2" + debug "4.1.1" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.2" + object.assign "4.1.0" + promise.allsettled "1.0.2" + serialize-javascript "4.0.0" + strip-json-comments "3.0.1" + supports-color "7.1.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.0" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.1" + +moment@2.30.1: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multer@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4.tgz#e2bc6cac0df57a8832b858d7418ccaa8ebaf7d8c" + integrity sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw== + dependencies: + append-field "^1.0.0" + busboy "^0.2.11" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-mocks-http@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.9.0.tgz#6000c570fc4b809603782309be81c73a71d85b71" + integrity sha512-ILf7Ws8xyX9Rl2fLZ7xhZBovrRwgaP84M13esndP6V17M/8j25TpwNzb7Im8U9XCo6fRhdwqiQajWXpsas/E6w== + dependencies: + accepts "^1.3.7" + depd "^1.1.0" + fresh "^0.5.2" + merge-descriptors "^1.0.1" + methods "^1.1.2" + mime "^1.3.4" + parseurl "^1.3.3" + range-parser "^1.2.0" + type-is "^1.6.18" + +nodemon@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.7.tgz#07cb1f455f8bece6a499e0d72b5e029485521a54" + integrity sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ== + dependencies: + chokidar "^3.5.2" + debug "^4" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^7.5.3" + simple-update-notifier "^2.0.0" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@^7.2.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== + dependencies: + abbrev "^2.0.0" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +oauth@0.10.x: + version "0.10.0" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.10.0.tgz#3551c4c9b95c53ea437e1e21e46b649482339c58" + integrity sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q== + +oauth@0.9.x: + version "0.9.15" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" + integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== + +object-assign@^4, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-keys@^1.0.11, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +on-finished@2.4.1, on-finished@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parseurl@^1.3.3, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +passport-google-oauth2@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/passport-google-oauth2/-/passport-google-oauth2-0.2.0.tgz#fc9ea59e7091f02e24fd16d6be9257ea982ebbc3" + integrity sha512-62EdPtbfVdc55nIXi0p1WOa/fFMM8v/M8uQGnbcXA4OexZWCnfsEi3wo2buag+Is5oqpuHzOtI64JpHk0Xi5RQ== + dependencies: + passport-oauth2 "^1.1.2" + +passport-jwt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.1.tgz#c443795eff322c38d173faa0a3c481479646ec3d" + integrity sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ== + dependencies: + jsonwebtoken "^9.0.0" + passport-strategy "^1.0.0" + +passport-microsoft@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/passport-microsoft/-/passport-microsoft-0.1.0.tgz#dc72c1a38b294d74f4dc55fe93f52e25cb9aa5b4" + integrity sha512-0giBDgE1fnR5X84zJZkQ11hnKVrzEgViwRO6RGsormK9zTxFQmN/UHMTDbIpvhk989VqALewB6Pk1R5vNr3GHw== + dependencies: + passport-oauth2 "1.2.0" + pkginfo "0.2.x" + +passport-oauth2@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.2.0.tgz#49613a3eca85c7a1e65bf1019e2b6b80a10c8ac2" + integrity sha512-6128N+n/MOrJdXxdC2q/PVKXtqgihGFIeup+9bsPybAvMPOUKqdGhh9ZIzZF8rFKJOlxUP9fgP3H0JQe18n0rg== + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + +passport-oauth2@^1.1.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.8.0.tgz#55725771d160f09bbb191828d5e3d559eee079c8" + integrity sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA== + dependencies: + base64url "3.x.x" + oauth "0.10.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + utils-merge "1.x.x" + +passport-strategy@1.x.x, passport-strategy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== + +passport@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.7.0.tgz#3688415a59a48cf8068417a8a8092d4492ca3a05" + integrity sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + utils-merge "^1.0.1" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +pause@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" + integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkginfo@0.2.x: + version "0.2.3" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.2.3.tgz#7239c42a5ef6c30b8f328439d9b9ff71042490f8" + integrity sha512-7W7wTrE/NsY8xv/DTGjwNIyNah81EQH0MWcTzrHL6pOpMocOGZc0Mbdz9aXxSrp+U0mSmkU8jrNCDCfUs3sOBg== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise.allsettled@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" + integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== + dependencies: + array.prototype.map "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + iterate-value "^1.0.0" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.0, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.2.2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regexp.prototype.flags@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve@^1.22.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.6.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.5, semver@^7.5.3, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + 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" + +sequelize-cli@6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-6.6.2.tgz#8d838b25c988cf136914cdc3843e19d88c3dcb67" + integrity sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg== + dependencies: + cli-color "^2.0.3" + fs-extra "^9.1.0" + js-beautify "^1.14.5" + lodash "^4.17.21" + resolve "^1.22.1" + umzug "^2.3.0" + yargs "^16.2.0" + +sequelize-json-schema@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sequelize-json-schema/-/sequelize-json-schema-2.1.1.tgz#a82d3813925e81485d76ce291f4ff5c8cb2ae492" + integrity sha512-yCGaHnmQQeL6MQ/fOxhkR5C2aOGZyTD6OrgjP4yw1rbuujuIUVdzWN3AsC6r6AvlGZ3EUBBbCJHKl8OIFFES4Q== + +serialize-javascript@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + 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" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-json-comments@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +supports-color@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tar@^6.1.11: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + 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" + +timers-ext@^0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.8.tgz#b4e442f10b7624a29dd2aa42c295e257150cf16c" + integrity sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww== + dependencies: + es5-ext "^0.10.64" + next-tick "^1.1.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +touch@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +type-is@^1.6.18, type-is@^1.6.4, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +uid2@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" + integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== + +umzug@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/umzug/-/umzug-2.3.0.tgz#0ef42b62df54e216b05dcaf627830a6a8b84a184" + integrity sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw== + dependencies: + bluebird "^3.7.2" + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + 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" + +which@2.0.2, which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +workerpool@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" + integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^15.0.1: + version "15.0.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.3.tgz#316e263d5febe8b38eef61ac092b33dfcc9b1115" + integrity sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.1.tgz#bd4b0ee05b4c94d058929c32cb09e3fce71d3c5f" + integrity sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA== + dependencies: + camelcase "^5.3.1" + decamelize "^1.2.0" + flat "^4.1.0" + is-plain-obj "^1.1.0" + yargs "^14.2.3" + +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@^14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.1" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + 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" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/backend/.sequelizerc b/backend/.sequelizerc new file mode 100644 index 0000000..fe89188 --- /dev/null +++ b/backend/.sequelizerc @@ -0,0 +1,7 @@ +const path = require('path'); +module.exports = { + "config": path.resolve("src", "db", "db.config.js"), + "models-path": path.resolve("src", "db", "models"), + "seeders-path": path.resolve("src", "db", "seeders"), + "migrations-path": path.resolve("src", "db", "migrations") +}; \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..581cb98 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,23 @@ +FROM node:20.15.1-alpine + +RUN apk update && apk add bash +# Create app directory +WORKDIR /usr/src/app + +# Install app dependencies +# A wildcard is used to ensure both package.json AND package-lock.json are copied +# where available (npm@5+) +COPY package*.json ./ + +RUN yarn install +# If you are building your code for production +# RUN npm ci --only=production + + +# Bundle app source +COPY . . + + +EXPOSE 8080 + +CMD [ "yarn", "start" ] diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..e18fb05 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,56 @@ + +#betterp - template backend, + +#### Run App on local machine: + +##### Install local dependencies: +- `yarn install` + +------------ + +##### Adjust local db: +###### 1. Install postgres: + - MacOS: + - `brew install postgres` + +- Ubuntu: + - `sudo apt update` + - `sudo apt install postgresql postgresql-contrib` + +###### 2. Create db and admin user: + - Before run and test connection, make sure you have created a database as described in the above configuration. You can use the `psql` command to create a user and database. + - `psql postgres --u postgres` + +- Next, type this command for creating a new user with password then give access for creating the database. + - `postgres-# CREATE ROLE admin WITH LOGIN PASSWORD 'admin_pass';` + - `postgres-# ALTER ROLE admin CREATEDB;` + +- Quit `psql` then log in again using the new user that previously created. + - `postgres-# \q` + - `psql postgres -U admin` + +- Type this command to creating a new database. + - `postgres=> CREATE DATABASE db_betterp;` + +- Then give that new user privileges to the new database then quit the `psql`. + - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_betterp TO admin;` + - `postgres=> \q` + +------------ + +#### Api Documentation (Swagger) + +http://localhost:8080/api-docs (local host) + +http://host_name/api-docs + +------------ + + ##### Setup database tables or update after schema change + - `yarn db:migrate` + + ##### Seed the initial data (admin accounts, relevant for the first setup): + - `yarn db:seed` + + ##### Start build: + - `yarn start` diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..67558e0 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,53 @@ +{ + "name": "betterp", + "description": "betterp - template backend", + "scripts": { + "start": "npm run db:migrate && npm run db:seed && npm run watch", + "db:migrate": "sequelize-cli db:migrate", + "db:seed": "sequelize-cli db:seed:all", + "db:drop": "sequelize-cli db:drop", + "db:create": "sequelize-cli db:create", + "watch": "node watcher.js" + }, + "dependencies": { + "@google-cloud/storage": "^5.18.2", + "axios": "^1.6.7", + "bcrypt": "5.1.1", + "chokidar": "^4.0.3", + "cors": "2.8.5", + "csv-parser": "^3.0.0", + "express": "4.18.2", + "formidable": "1.2.2", + "helmet": "4.1.1", + "json2csv": "^5.0.7", + "jsonwebtoken": "8.5.1", + "lodash": "4.17.21", + "moment": "2.30.1", + "multer": "^1.4.4", + "mysql2": "2.2.5", + "nodemailer": "6.9.9", + "passport": "^0.7.0", + "passport-google-oauth2": "^0.2.0", + "passport-jwt": "^4.0.1", + "passport-microsoft": "^0.1.0", + "pg": "8.4.1", + "pg-hstore": "2.3.4", + "sequelize": "6.35.2", + "sequelize-json-schema": "^2.1.1", + "sqlite": "4.0.15", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.0", + "tedious": "^18.2.4" + }, + "engines": { + "node": ">=18" + }, + "private": true, + "devDependencies": { + "cross-env": "7.0.3", + "mocha": "8.1.3", + "node-mocks-http": "1.9.0", + "nodemon": "2.0.5", + "sequelize-cli": "6.6.2" + } +} diff --git a/backend/src/auth/auth.js b/backend/src/auth/auth.js new file mode 100644 index 0000000..5cbb79a --- /dev/null +++ b/backend/src/auth/auth.js @@ -0,0 +1,27 @@ +const config = require('../config'); + +const passport = require('passport'); +const JWTstrategy = require('passport-jwt').Strategy; +const ExtractJWT = require('passport-jwt').ExtractJwt; +const UsersDBApi = require('../db/api/users'); + + +passport.use(new JWTstrategy({ + passReqToCallback: true, + secretOrKey: config.secret_key, + jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken() +}, async (req, token, done) => { + try { + const user = await UsersDBApi.findBy( {email: token.user.email}); + + if (user && user.disabled) { + return done (new Error(`User '${user.email}' is disabled`)); + } + + req.currentUser = user; + + return done(null, user); + } catch (error) { + done(error); + } +})); \ No newline at end of file diff --git a/backend/src/config.js b/backend/src/config.js new file mode 100644 index 0000000..d41f58c --- /dev/null +++ b/backend/src/config.js @@ -0,0 +1,59 @@ + + +const os = require('os'); + +const config = { + gcloud: { + bucket: "fldemo-files", + hash: "d98a0f74f8be4eaf59a8cc616febbbbf" + }, + bcrypt: { + saltRounds: 12 + }, + admin_pass: "3b603a71", + user_pass: "9539bcd4f7bb", + admin_email: "admin@flatlogic.com", + providers: { + LOCAL: 'local', + GOOGLE: 'google', + MICROSOFT: 'microsoft' + }, + secret_key: process.env.SECRET_KEY || '', + remote: '', + port: process.env.NODE_ENV === "production" ? "" : "8080", + hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost", + portUI: process.env.NODE_ENV === "production" ? "" : "3000", + portUIProd: process.env.NODE_ENV === "production" ? "" : ":3000", + swaggerUI: process.env.NODE_ENV === "production" ? "" : "http://localhost", + swaggerPort: process.env.NODE_ENV === "production" ? "" : ":8080", + + uploadDir: os.tmpdir(), + email: { + from: 'betterp ', + host: 'email-smtp.us-east-1.amazonaws.com', + port: 587, + auth: { + user: process.env.EMAIL_USER || '', + pass: process.env.EMAIL_PASS, + }, + tls: { + rejectUnauthorized: false + } + }, + roles: { + admin: 'Administrator', + user: 'User', + }, + + project_uuid: '3b603a71-a1c3-41f0-8f99-9539bcd4f7bb', + flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects', + +}; + +config.host = process.env.NODE_ENV === "production" ? config.remote : "http://localhost"; +config.apiUrl = `${config.host}${config.port ? `:${config.port}` : ``}/api`; +config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`; +config.uiUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}/#`; +config.backUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}`; + +module.exports = config; diff --git a/backend/src/db/api/users.js b/backend/src/db/api/users.js new file mode 100644 index 0000000..8b4828e --- /dev/null +++ b/backend/src/db/api/users.js @@ -0,0 +1,692 @@ + +const db = require('../models'); +const crypto = require('crypto'); +const Utils = require('../utils'); + +const bcrypt = require('bcrypt'); +const config = require('../../config'); + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +module.exports = class UsersDBApi { + + static async create(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + const users = await db.users.create( + { + id: data.data.id || undefined, + + firstName: data.data.firstName + || + null, + + lastName: data.data.lastName + || + null, + + phoneNumber: data.data.phoneNumber + || + null, + + email: data.data.email + || + null, + + disabled: data.data.disabled + || + false + , + + password: data.data.password + || + null, + + emailVerified: data.data.emailVerified + || + true + , + + emailVerificationToken: data.data.emailVerificationToken + || + null, + + emailVerificationTokenExpiresAt: data.data.emailVerificationTokenExpiresAt + || + null, + + passwordResetToken: data.data.passwordResetToken + || + null, + + passwordResetTokenExpiresAt: data.data.passwordResetTokenExpiresAt + || + null, + + provider: data.data.provider + || + null, + + importHash: data.data.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + return users; + } + + static async bulkImport(data, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + // Prepare data - wrapping individual data transformations in a map() method + const usersData = data.map((item, index) => ({ + id: item.id || undefined, + + firstName: item.firstName + || + null + , + + lastName: item.lastName + || + null + , + + phoneNumber: item.phoneNumber + || + null + , + + email: item.email + || + null + , + + disabled: item.disabled + || + false + + , + + password: item.password + || + null + , + + emailVerified: item.emailVerified + || + false + + , + + emailVerificationToken: item.emailVerificationToken + || + null + , + + emailVerificationTokenExpiresAt: item.emailVerificationTokenExpiresAt + || + null + , + + passwordResetToken: item.passwordResetToken + || + null + , + + passwordResetTokenExpiresAt: item.passwordResetTokenExpiresAt + || + null + , + + provider: item.provider + || + null + , + + importHash: item.importHash || null, + createdById: currentUser.id, + updatedById: currentUser.id, + createdAt: new Date(Date.now() + index * 1000), + })); + + // Bulk create items + const users = await db.users.bulkCreate(usersData, { transaction }); + + return users; + } + + static async update(id, data, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + const users = await db.users.findByPk(id, {}, {transaction}); + + if (!data?.app_role) { + data.app_role = users?.app_role?.id; + } + if (!data?.custom_permissions) { + data.custom_permissions = users?.custom_permissions?.map(item => item.id); + } + + if (data.password) { + data.password = bcrypt.hashSync( + data.password, + config.bcrypt.saltRounds, + ); + } else { + data.password = users.password; + } + + const updatePayload = {}; + + if (data.firstName !== undefined) updatePayload.firstName = data.firstName; + + if (data.lastName !== undefined) updatePayload.lastName = data.lastName; + + if (data.phoneNumber !== undefined) updatePayload.phoneNumber = data.phoneNumber; + + if (data.email !== undefined) updatePayload.email = data.email; + + if (data.disabled !== undefined) updatePayload.disabled = data.disabled; + + if (data.password !== undefined) updatePayload.password = data.password; + + if (data.emailVerified !== undefined) updatePayload.emailVerified = data.emailVerified; + + else updatePayload.emailVerified = true; + + if (data.emailVerificationToken !== undefined) updatePayload.emailVerificationToken = data.emailVerificationToken; + + if (data.emailVerificationTokenExpiresAt !== undefined) updatePayload.emailVerificationTokenExpiresAt = data.emailVerificationTokenExpiresAt; + + if (data.passwordResetToken !== undefined) updatePayload.passwordResetToken = data.passwordResetToken; + + if (data.passwordResetTokenExpiresAt !== undefined) updatePayload.passwordResetTokenExpiresAt = data.passwordResetTokenExpiresAt; + + if (data.provider !== undefined) updatePayload.provider = data.provider; + + updatePayload.updatedById = currentUser.id; + + await users.update(updatePayload, {transaction}); + + return users; + } + + static async deleteByIds(ids, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const users = await db.users.findAll({ + where: { + id: { + [Op.in]: ids, + }, + }, + transaction, + }); + + await db.sequelize.transaction(async (transaction) => { + for (const record of users) { + await record.update( + {deletedBy: currentUser.id}, + {transaction} + ); + } + for (const record of users) { + await record.destroy({transaction}); + } + }); + + return users; + } + + static async remove(id, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + + const users = await db.users.findByPk(id, options); + + await users.update({ + deletedBy: currentUser.id + }, { + transaction, + }); + + await users.destroy({ + transaction + }); + + return users; + } + + static async findBy(where, options) { + const transaction = (options && options.transaction) || undefined; + + const users = await db.users.findOne( + { where }, + { transaction }, + ); + + if (!users) { + return users; + } + + const output = users.get({plain: true}); + + return output; + } + + static async findAll(filter, options) { + const limit = filter.limit || 0; + let offset = 0; + let where = {}; + const currentPage = +filter.page; + + const user = (options && options.currentUser) || null; + + offset = currentPage * limit; + + const orderBy = null; + + const transaction = (options && options.transaction) || undefined; + + let include = []; + + if (filter) { + if (filter.id) { + where = { + ...where, + ['id']: Utils.uuid(filter.id), + }; + } + + if (filter.firstName) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'firstName', + filter.firstName, + ), + }; + } + + if (filter.lastName) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'lastName', + filter.lastName, + ), + }; + } + + if (filter.phoneNumber) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'phoneNumber', + filter.phoneNumber, + ), + }; + } + + if (filter.email) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'email', + filter.email, + ), + }; + } + + if (filter.password) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'password', + filter.password, + ), + }; + } + + if (filter.emailVerificationToken) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'emailVerificationToken', + filter.emailVerificationToken, + ), + }; + } + + if (filter.passwordResetToken) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'passwordResetToken', + filter.passwordResetToken, + ), + }; + } + + if (filter.provider) { + where = { + ...where, + [Op.and]: Utils.ilike( + 'users', + 'provider', + filter.provider, + ), + }; + } + + if (filter.emailVerificationTokenExpiresAtRange) { + const [start, end] = filter.emailVerificationTokenExpiresAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + emailVerificationTokenExpiresAt: { + ...where.emailVerificationTokenExpiresAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + emailVerificationTokenExpiresAt: { + ...where.emailVerificationTokenExpiresAt, + [Op.lte]: end, + }, + }; + } + } + + if (filter.passwordResetTokenExpiresAtRange) { + const [start, end] = filter.passwordResetTokenExpiresAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + passwordResetTokenExpiresAt: { + ...where.passwordResetTokenExpiresAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + passwordResetTokenExpiresAt: { + ...where.passwordResetTokenExpiresAt, + [Op.lte]: end, + }, + }; + } + } + + if (filter.active !== undefined) { + where = { + ...where, + active: filter.active === true || filter.active === 'true' + }; + } + + if (filter.disabled) { + where = { + ...where, + disabled: filter.disabled, + }; + } + + if (filter.emailVerified) { + where = { + ...where, + emailVerified: filter.emailVerified, + }; + } + + if (filter.createdAtRange) { + const [start, end] = filter.createdAtRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + ['createdAt']: { + ...where.createdAt, + [Op.lte]: end, + }, + }; + } + } + } + + const queryOptions = { + where, + include, + distinct: true, + order: filter.field && filter.sort + ? [[filter.field, filter.sort]] + : [['createdAt', 'desc']], + transaction: options?.transaction, + logging: console.log + }; + + if (!options?.countOnly) { + queryOptions.limit = limit ? Number(limit) : undefined; + queryOptions.offset = offset ? Number(offset) : undefined; + } + + try { + const { rows, count } = await db.users.findAndCountAll(queryOptions); + + return { + rows: options?.countOnly ? [] : rows, + count: count + }; + } catch (error) { + console.error('Error executing query:', error); + throw error; + } + } + + static async findAllAutocomplete(query, limit, offset) { + let where = {}; + + if (query) { + where = { + [Op.or]: [ + { ['id']: Utils.uuid(query) }, + Utils.ilike( + 'users', + 'firstName', + query, + ), + ], + }; + } + + const records = await db.users.findAll({ + attributes: [ 'id', 'firstName' ], + where, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + orderBy: [['firstName', 'ASC']], + }); + + return records.map((record) => ({ + id: record.id, + label: record.firstName, + })); + } + + static async createFromAuth(data, options) { + const transaction = (options && options.transaction) || undefined; + const users = await db.users.create( + { + email: data.email, + firstName: data.firstName, + authenticationUid: data.authenticationUid, + password: data.password, + }, + { transaction }, + ); + + const app_role = await db.roles.findOne({ + where: { name: config.roles?.user || "User" }, + }); + if (app_role?.id) { + await users.setApp_role(app_role?.id || null, { + transaction, + }); + } + + await users.update( + { + authenticationUid: users.id, + }, + { transaction }, + ); + + delete users.password; + return users; + } + + static async updatePassword(id, password, options) { + const currentUser = (options && options.currentUser) || { id: null }; + + const transaction = (options && options.transaction) || undefined; + + const users = await db.users.findByPk(id, { + transaction, + }); + + await users.update( + { + password, + authenticationUid: id, + updatedById: currentUser.id, + }, + { transaction }, + ); + + return users; + } + + static async generateEmailVerificationToken(email, options) { + return this._generateToken(['emailVerificationToken', 'emailVerificationTokenExpiresAt'], email, options); + } + + static async generatePasswordResetToken(email, options) { + return this._generateToken(['passwordResetToken', 'passwordResetTokenExpiresAt'], email, options); + } + + static async findByPasswordResetToken(token, options) { + const transaction = (options && options.transaction) || undefined; + + return db.users.findOne( + { + where: { + passwordResetToken: token, + passwordResetTokenExpiresAt: { + [db.Sequelize.Op.gt]: Date.now(), + }, + }, + }, + { transaction }, + ); + } + + static async findByEmailVerificationToken(token, options) { + const transaction = (options && options.transaction) || undefined; + return db.users.findOne( + { + where: { + emailVerificationToken: token, + emailVerificationTokenExpiresAt: { + [db.Sequelize.Op.gt]: Date.now(), + }, + }, + }, + { transaction }, + ); + } + + static async markEmailVerified(id, options) { + const currentUser = (options && options.currentUser) || { id: null }; + const transaction = (options && options.transaction) || undefined; + + const users = await db.users.findByPk(id, { + transaction, + }); + + await users.update( + { + emailVerified: true, + updatedById: currentUser.id, + }, + { transaction }, + ); + + return true; + } + + static async _generateToken(keyNames, email, options) { + const currentUser = (options && options.currentUser) || {id: null}; + const transaction = (options && options.transaction) || undefined; + const users = await db.users.findOne( + { + where: { email: email.toLowerCase() }, + }, + { + transaction, + }, + ); + + const token = crypto + .randomBytes(20) + .toString('hex'); + const tokenExpiresAt = Date.now() + 360000; + + if(users){ + await users.update( + { + [keyNames[0]]: token, + [keyNames[1]]: tokenExpiresAt, + updatedById: currentUser.id, + }, + {transaction}, + ); + } + + return token; + } + +}; + diff --git a/backend/src/db/db.config.js b/backend/src/db/db.config.js new file mode 100644 index 0000000..d4ec834 --- /dev/null +++ b/backend/src/db/db.config.js @@ -0,0 +1,33 @@ + + +module.exports = { + production: { + dialect: 'postgres', + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + logging: console.log, + seederStorage: 'sequelize', + }, + development: { + username: 'postgres', + dialect: 'postgres', + password: '', + database: 'db_betterp', + host: process.env.DB_HOST || 'localhost', + logging: console.log, + seederStorage: 'sequelize', + }, + dev_stage: { + dialect: 'postgres', + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + logging: console.log, + seederStorage: 'sequelize', + } +}; diff --git a/backend/src/db/migrations/1758775130656.js b/backend/src/db/migrations/1758775130656.js new file mode 100644 index 0000000..3a66341 --- /dev/null +++ b/backend/src/db/migrations/1758775130656.js @@ -0,0 +1,268 @@ +module.exports = { + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async up(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + + await queryInterface.createTable('users', { + id: { + type: Sequelize.DataTypes.UUID, + defaultValue: Sequelize.DataTypes.UUIDV4, + primaryKey: true, + }, + createdById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + updatedById: { + type: Sequelize.DataTypes.UUID, + references: { + key: 'id', + model: 'users', + }, + }, + createdAt: { type: Sequelize.DataTypes.DATE }, + updatedAt: { type: Sequelize.DataTypes.DATE }, + deletedAt: { type: Sequelize.DataTypes.DATE }, + importHash: { + type: Sequelize.DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, { transaction }); + + await queryInterface.addColumn( + 'users', + 'firstName', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'lastName', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'phoneNumber', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'email', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'disabled', + { + type: Sequelize.DataTypes.BOOLEAN, + + defaultValue: false, + allowNull: false, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'password', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'emailVerified', + { + type: Sequelize.DataTypes.BOOLEAN, + + defaultValue: false, + allowNull: false, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'emailVerificationToken', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'emailVerificationTokenExpiresAt', + { + type: Sequelize.DataTypes.DATE, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'passwordResetToken', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'passwordResetTokenExpiresAt', + { + type: Sequelize.DataTypes.DATE, + + }, + { transaction } + ); + + await queryInterface.addColumn( + 'users', + 'provider', + { + type: Sequelize.DataTypes.TEXT, + + }, + { transaction } + ); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + }, + /** + * @param {QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @returns {Promise} + */ + async down(queryInterface, Sequelize) { + /** + * @type {Transaction} + */ + const transaction = await queryInterface.sequelize.transaction(); + try { + + await queryInterface.removeColumn( + 'users', + 'provider', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'passwordResetTokenExpiresAt', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'passwordResetToken', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'emailVerificationTokenExpiresAt', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'emailVerificationToken', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'emailVerified', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'password', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'disabled', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'email', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'phoneNumber', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'lastName', + { transaction } + ); + + await queryInterface.removeColumn( + 'users', + 'firstName', + { transaction } + ); + + await queryInterface.dropTable('users', { transaction }); + + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + } +}; diff --git a/backend/src/db/models/index.js b/backend/src/db/models/index.js new file mode 100644 index 0000000..4a3852f --- /dev/null +++ b/backend/src/db/models/index.js @@ -0,0 +1,38 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); +const basename = path.basename(__filename); +const env = process.env.NODE_ENV || 'development'; +const config = require("../db.config")[env]; +const db = {}; + +let sequelize; +console.log(env); +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +fs + .readdirSync(__dirname) + .filter(file => { + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); + }) + .forEach(file => { + const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes) + db[model.name] = model; + }); + +Object.keys(db).forEach(modelName => { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/backend/src/db/models/users.js b/backend/src/db/models/users.js new file mode 100644 index 0000000..aad4d4f --- /dev/null +++ b/backend/src/db/models/users.js @@ -0,0 +1,148 @@ +const config = require('../../config'); +const providers = config.providers; +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const moment = require('moment'); + +module.exports = function(sequelize, DataTypes) { + const users = sequelize.define( + 'users', + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + +firstName: { + type: DataTypes.TEXT, + + }, + +lastName: { + type: DataTypes.TEXT, + + }, + +phoneNumber: { + type: DataTypes.TEXT, + + }, + +email: { + type: DataTypes.TEXT, + + }, + +disabled: { + type: DataTypes.BOOLEAN, + + allowNull: false, + defaultValue: false, + + }, + +password: { + type: DataTypes.TEXT, + + }, + +emailVerified: { + type: DataTypes.BOOLEAN, + + allowNull: false, + defaultValue: false, + + }, + +emailVerificationToken: { + type: DataTypes.TEXT, + + }, + +emailVerificationTokenExpiresAt: { + type: DataTypes.DATE, + + }, + +passwordResetToken: { + type: DataTypes.TEXT, + + }, + +passwordResetTokenExpiresAt: { + type: DataTypes.DATE, + + }, + +provider: { + type: DataTypes.TEXT, + + }, + + importHash: { + type: DataTypes.STRING(255), + allowNull: true, + unique: true, + }, + }, + { + timestamps: true, + paranoid: true, + freezeTableName: true, + }, + ); + + users.associate = (db) => { + + db.users.belongsTo(db.users, { + as: 'createdBy', + }); + + db.users.belongsTo(db.users, { + as: 'updatedBy', + }); + }; + + users.beforeCreate((users, options) => { + users = trimStringFields(users); + + if (users.provider !== providers.LOCAL && Object.values(providers).indexOf(users.provider) > -1) { + users.emailVerified = true; + + if (!users.password) { + const password = crypto + .randomBytes(20) + .toString('hex'); + + const hashedPassword = bcrypt.hashSync( + password, + config.bcrypt.saltRounds, + ); + + users.password = hashedPassword + } + } + }); + + users.beforeUpdate((users, options) => { + users = trimStringFields(users); + }); + + return users; +}; + +function trimStringFields(users) { + users.email = users.email.trim(); + + users.firstName = users.firstName + ? users.firstName.trim() + : null; + + users.lastName = users.lastName + ? users.lastName.trim() + : null; + + return users; +} + diff --git a/backend/src/db/reset.js b/backend/src/db/reset.js new file mode 100644 index 0000000..5904d4b --- /dev/null +++ b/backend/src/db/reset.js @@ -0,0 +1,16 @@ +const db = require('./models'); +const {execSync} = require("child_process"); + +console.log('Resetting Database'); + +db.sequelize + .sync({ force: true }) + .then(() => { + execSync("sequelize db:seed:all"); + console.log('OK'); + process.exit(); + }) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/backend/src/db/seeders/20200430130759-admin-user.js b/backend/src/db/seeders/20200430130759-admin-user.js new file mode 100644 index 0000000..018685e --- /dev/null +++ b/backend/src/db/seeders/20200430130759-admin-user.js @@ -0,0 +1,66 @@ +'use strict'; +const bcrypt = require("bcrypt"); +const config = require("../../config"); + +const ids = [ + '193bf4b5-9f07-4bd5-9a43-e7e41f3e96af', + 'af5a87be-8f9c-4630-902a-37a60b7005ba', + '5bc531ab-611f-41f3-9373-b7cc5d09c93d', +] + +module.exports = { + up: async (queryInterface, Sequelize) => { + let admin_hash = bcrypt.hashSync(config.admin_pass, config.bcrypt.saltRounds); + let user_hash = bcrypt.hashSync(config.user_pass, config.bcrypt.saltRounds); + + try { + await queryInterface.bulkInsert('users', [ + { + id: ids[0], + firstName: 'Admin', + email: config.admin_email, + emailVerified: true, + provider: config.providers.LOCAL, + password: admin_hash, + createdAt: new Date(), + updatedAt: new Date() + }, + { + id: ids[1], + firstName: 'John', + email: 'john@doe.com', + emailVerified: true, + provider: config.providers.LOCAL, + password: user_hash, + createdAt: new Date(), + updatedAt: new Date() + }, + { + id: ids[2], + firstName: 'Client', + email: 'client@hello.com', + emailVerified: true, + provider: config.providers.LOCAL, + password: user_hash, + createdAt: new Date(), + updatedAt: new Date() + }, + ]); + } catch (error) { + console.error('Error during bulkInsert:', error); + throw error; + } + }, + down: async (queryInterface, Sequelize) => { + try { + await queryInterface.bulkDelete('users', { + id: { + [Sequelize.Op.in]: ids, + }, + }, {}); + } catch (error) { + console.error('Error during bulkDelete:', error); + throw error; + } +} +} diff --git a/backend/src/db/utils.js b/backend/src/db/utils.js new file mode 100644 index 0000000..c253a07 --- /dev/null +++ b/backend/src/db/utils.js @@ -0,0 +1,27 @@ +const validator = require('validator'); +const { v4: uuid } = require('uuid'); +const Sequelize = require('./models').Sequelize; + +module.exports = class Utils { + static uuid(value) { + let id = value; + + if (!validator.isUUID(id)) { + id = uuid(); + } + + return id; + } + + static ilike(model, column, value) { + return Sequelize.where( + Sequelize.fn( + 'lower', + Sequelize.col(`${model}.${column}`), + ), + { + [Sequelize.Op.like]: `%${value}%`.toLowerCase(), + }, + ); + } +}; diff --git a/backend/src/helpers.js b/backend/src/helpers.js new file mode 100644 index 0000000..c38440d --- /dev/null +++ b/backend/src/helpers.js @@ -0,0 +1,23 @@ +const jwt = require('jsonwebtoken'); +const config = require('./config'); + +module.exports = class Helpers { + static wrapAsync(fn) { + return function (req, res, next) { + fn(req, res, next).catch(next); + }; + } + + static commonErrorHandler(error, req, res, next) { + if ([400, 403, 404].includes(error.code)) { + return res.status(error.code).send(error.message); + } + + console.error(error); + return res.status(500).send(error.message); + } + + static jwtSign(data) { + return jwt.sign(data, config.secret_key, {expiresIn: '6h'}); + }; +}; diff --git a/backend/src/index.js b/backend/src/index.js new file mode 100644 index 0000000..4cdf555 --- /dev/null +++ b/backend/src/index.js @@ -0,0 +1,107 @@ + +const express = require('express'); +const cors = require('cors'); +const app = express(); +const passport = require('passport'); +const path = require('path'); +const fs = require('fs'); +const bodyParser = require('body-parser'); +const db = require('./db/models'); +const config = require('./config'); +const swaggerUI = require('swagger-ui-express'); +const swaggerJsDoc = require('swagger-jsdoc'); + +const authRoutes = require('./routes/auth'); +const searchRoutes = require('./routes/search'); + +const contactFormRoutes = require('./routes/contactForm'); + +const usersRoutes = require('./routes/users'); + +const getBaseUrl = (url) => { + if (!url) return ''; + return url.endsWith('/api') ? url.slice(0, -4) : url; +}; + +const options = { + definition: { + openapi: "3.0.0", + info: { + version: "1.0.0", + title: "betterp", + description: "betterp Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.", + }, + servers: [ + { + url: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || config.swaggerUrl, + description: "Development server", + } + ], + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + } + }, + responses: { + UnauthorizedError: { + description: "Access token is missing or invalid" + } + } + }, + security: [{ + bearerAuth: [] + }] + }, + apis: ["./src/routes/*.js"], +}; + +const specs = swaggerJsDoc(options); +app.use('/api-docs', function (req, res, next) { + swaggerUI.host = getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || req.get('host'); + next() + }, swaggerUI.serve, swaggerUI.setup(specs)) + +app.use(cors({origin: true})); +require('./auth/auth'); + +app.use(bodyParser.json()); + +app.use('/api/auth', authRoutes); +app.enable('trust proxy'); + +app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes); + +app.use('/api/contact-form', contactFormRoutes); + +app.use( + '/api/search', + passport.authenticate('jwt', { session: false }), + searchRoutes); + +const publicDir = path.join( + __dirname, + '../public', +); + +if (fs.existsSync(publicDir)) { + app.use('/', express.static(publicDir)); + + app.get('*', function(request, response) { + response.sendFile( + path.resolve(publicDir, 'index.html'), + ); + }); +} + +const PORT = process.env.NODE_ENV === 'dev_stage' ? 3000 : 8080; + +db.sequelize.sync().then(function () { + app.listen(PORT, () => { + console.log(`Listening on port ${PORT}`); + }); +}); + +module.exports = app; diff --git a/backend/src/middlewares/upload.js b/backend/src/middlewares/upload.js new file mode 100644 index 0000000..ea3e835 --- /dev/null +++ b/backend/src/middlewares/upload.js @@ -0,0 +1,11 @@ +const util = require('util'); +const Multer = require('multer'); +const maxSize = 10 * 1024 * 1024; + +let processFile = Multer({ + storage: Multer.memoryStorage(), + limits: { fileSize: maxSize }, +}).single("file"); + +let processFileMiddleware = util.promisify(processFile); +module.exports = processFileMiddleware; diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js new file mode 100644 index 0000000..84d08d1 --- /dev/null +++ b/backend/src/routes/auth.js @@ -0,0 +1,200 @@ +const express = require('express'); +const passport = require('passport'); + +const config = require('../config'); +const AuthService = require('../services/auth'); +const ForbiddenError = require('../services/notifications/errors/forbidden'); +const wrapAsync = require('../helpers').wrapAsync; + +const router = express.Router(); + +/** + * @swagger + * components: + * schemas: + * Auth: + * type: object + * required: + * - email + * - password + * properties: + * email: + * type: string + * default: admin@flatlogic.com + * description: User email + * password: + * type: string + * default: password + * description: User password + */ + +/** + * @swagger + * tags: + * name: Auth + * description: Authorization operations + */ + +/** + * @swagger + * /api/auth/signin/local: + * post: + * tags: [Auth] + * summary: Logs user into the system + * description: Logs user into the system + * requestBody: + * description: Set valid user email and password + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Auth" + * responses: + * 200: + * description: Successful login + * 400: + * description: Invalid username/password supplied + * x-codegen-request-body-name: body + */ + +router.post('/signin/local', wrapAsync(async (req, res) => { + const payload = await AuthService.signin(req.body.email, req.body.password, req,); + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/auth/me: + * get: + * security: + * - bearerAuth: [] + * tags: [Auth] + * summary: Get current authorized user info + * description: Get current authorized user info + * responses: + * 200: + * description: Successful retrieval of current authorized user data + * 400: + * description: Invalid username/password supplied + * x-codegen-request-body-name: body + */ + +router.get('/me', passport.authenticate('jwt', {session: false}), (req, res) => { + if (!req.currentUser || !req.currentUser.id) { + throw new ForbiddenError(); + } + + const payload = req.currentUser; + delete payload.password; + res.status(200).send(payload); +}); + +router.put('/password-reset', wrapAsync(async (req, res) => { + const payload = await AuthService.passwordReset(req.body.token, req.body.password, req,); + res.status(200).send(payload); +})); + +router.put('/password-update', passport.authenticate('jwt', {session: false}), wrapAsync(async (req, res) => { + const payload = await AuthService.passwordUpdate(req.body.currentPassword, req.body.newPassword, req); + res.status(200).send(payload); +})); + +router.post('/send-email-address-verification-email', passport.authenticate('jwt', {session: false}), wrapAsync(async (req, res) => { + if (!req.currentUser) { + throw new ForbiddenError(); + } + + await AuthService.sendEmailAddressVerificationEmail(req.currentUser.email); + const payload = true; + res.status(200).send(payload); +})); + +router.post('/send-password-reset-email', wrapAsync(async (req, res) => { + const link = new URL(req.headers.referer); + await AuthService.sendPasswordResetEmail(req.body.email, 'register', link.host,); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/auth/signup: + * post: + * tags: [Auth] + * summary: Register new user into the system + * description: Register new user into the system + * requestBody: + * description: Set valid user email and password + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Auth" + * responses: + * 200: + * description: New user successfully signed up + * 400: + * description: Invalid username/password supplied + * 500: + * description: Some server error + * x-codegen-request-body-name: body + */ + +router.post('/signup', wrapAsync(async (req, res) => { + const link = new URL(req.headers.referer); + const payload = await AuthService.signup( + req.body.email, + req.body.password, + req, + link.host, + ) + res.status(200).send(payload); +})); + +router.put('/profile', passport.authenticate('jwt', {session: false}), wrapAsync(async (req, res) => { + if (!req.currentUser || !req.currentUser.id) { + throw new ForbiddenError(); + } + + await AuthService.updateProfile(req.body.profile, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +router.put('/verify-email', wrapAsync(async (req, res) => { + const payload = await AuthService.verifyEmail(req.body.token, req, req.headers.referer) + res.status(200).send(payload); +})); + +router.get('/signin/google', (req, res, next) => { + passport.authenticate("google", {scope: ["profile", "email"], state: req.query.app})(req, res, next); +}); + +router.get('/signin/google/callback', passport.authenticate("google", {failureRedirect: "/login", session: false}), + + function (req, res) { + socialRedirect(res, req.query.state, req.user.token, config); + } +); + +router.get('/signin/microsoft', (req, res, next) => { + passport.authenticate("microsoft", { + scope: ["https://graph.microsoft.com/user.read openid"], + state: req.query.app + })(req, res, next); +}); + +router.get('/signin/microsoft/callback', passport.authenticate("microsoft", { + failureRedirect: "/login", + session: false + }), + function (req, res) { + socialRedirect(res, req.query.state, req.user.token, config); + } +); + +router.use('/', require('../helpers').commonErrorHandler); + +function socialRedirect(res, state, token, config) { + res.redirect(config.uiUrl + "/login?token=" + token); +} + +module.exports = router; diff --git a/backend/src/routes/contactForm.js b/backend/src/routes/contactForm.js new file mode 100644 index 0000000..3f4da9b --- /dev/null +++ b/backend/src/routes/contactForm.js @@ -0,0 +1,22 @@ + +const express = require('express'); +const router = express.Router(); + +router.post('/send', async (req, res) => { + try { + const { email, subject, message } = req.body; + + if (!email || !subject || !message) { + return res.status(400).json({ error: 'All fields are required' }); + } + + // Generate the email content + // Send the email + res.status(200).json({ message: 'Email sent successfully' }); + } catch (error) { + console.error('Error sending email:', error); + res.status(500).json({ error: 'Error sending email' }); + } + }); + + module.exports = router; diff --git a/backend/src/routes/file.js b/backend/src/routes/file.js new file mode 100644 index 0000000..ddd2bc0 --- /dev/null +++ b/backend/src/routes/file.js @@ -0,0 +1,32 @@ +const express = require('express'); +const config = require('../config'); +const path = require('path'); +const passport = require('passport'); +const services = require('../services/file'); +const router = express.Router(); + +router.get('/download', (req, res) => { + if (process.env.NODE_ENV == "production" || process.env.NEXT_PUBLIC_BACK_API) { + services.downloadGCloud(req, res); + } + else { + services.downloadLocal(req, res); + } +}); + +router.post('/upload/:table/:field', passport.authenticate('jwt', {session: false}), (req, res) => { + const fileName = `${req.params.table}/${req.params.field}`; + + if (process.env.NODE_ENV == "production" || process.env.NEXT_PUBLIC_BACK_API) { + services.uploadGCloud(fileName, req, res); + } + else { + services.uploadLocal(fileName, { + entity: null, + maxFileSize: 10 * 1024 * 1024, + folderIncludesAuthenticationUid: false, + })(req, res); + } +}); + +module.exports = router; diff --git a/backend/src/routes/openai.js b/backend/src/routes/openai.js new file mode 100644 index 0000000..6fa17ba --- /dev/null +++ b/backend/src/routes/openai.js @@ -0,0 +1,251 @@ +const express = require('express'); +const db = require('../db/models'); +const wrapAsync = require('../helpers').wrapAsync; +const router = express.Router(); +const sjs = require('sequelize-json-schema'); +const { getWidget, askGpt } = require('../services/openai'); +const RolesService = require('../services/roles'); +const RolesDBApi = require("../db/api/roles"); + +/** + * @swagger + * /api/roles/roles-info/{infoId}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Roles] + * summary: Remove role information by ID + * description: Remove specific role information by ID + * parameters: + * - in: path + * name: infoId + * description: ID of role information to remove + * required: true + * schema: + * type: string + * - in: query + * name: userId + * description: ID of the user + * required: true + * schema: + * type: string + * - in: query + * name: key + * description: Key of the role information to remove + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Role information successfully removed + * content: + * application/json: + * schema: + * type: object + * properties: + * user: + * type: string + * description: The user information + * 400: + * description: Invalid ID or key supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Role not found + * 500: + * description: Some server error + */ + +router.delete( + '/roles-info/:infoId', + wrapAsync(async (req, res) => { + const role = await RolesService.removeRoleInfoById( + req.query.infoId, + req.query.roleId, + req.query.key, + req.currentUser, + ); + + res.status(200).send(role); + }), +); + +/** + * @swagger + * /api/roles/role-info/{roleId}: + * get: + * security: + * - bearerAuth: [] + * tags: [Roles] + * summary: Get role information by key + * description: Get specific role information by key + * parameters: + * - in: path + * name: roleId + * description: ID of role to get information for + * required: true + * schema: + * type: string + * - in: query + * name: key + * description: Key of the role information to retrieve + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Role information successfully received + * content: + * application/json: + * schema: + * type: object + * properties: + * info: + * type: string + * description: The role information + * 400: + * description: Invalid ID or key supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Role not found + * 500: + * description: Some server error + */ + +router.get( + '/info-by-key', + wrapAsync(async (req, res) => { + const roleId = req.query.roleId; + const key = req.query.key; + const currentUser = req.currentUser; + let info = await RolesService.getRoleInfoByKey( + key, + roleId, + currentUser, + ); + const role = await RolesDBApi.findBy({ id: roleId }); + if (!role?.role_customization) { + await Promise.all(["pie","bar"].map(async (e)=>{ + const schema = await sjs.getSequelizeSchema(db.sequelize, {}); + const payload = { + description: `Create some cool ${e} chart`, + modelDefinition: schema.definitions, + }; + const widgetId = await getWidget(payload, currentUser?.id, roleId); + if(widgetId){ + await RolesService.addRoleInfo( + roleId, + currentUser?.id, + 'widgets', + widgetId, + req.currentUser, + ); + } + })) + info = await RolesService.getRoleInfoByKey( + key, + roleId, + currentUser, + ); + } + res.status(200).send(info); + }), +); + +router.post( + '/create_widget', + wrapAsync(async (req, res) => { + const { description, userId, roleId } = req.body; + + const currentUser = req.currentUser; + const schema = await sjs.getSequelizeSchema(db.sequelize, {}); + const payload = { + description, + modelDefinition: schema.definitions, + }; + + const widgetId = await getWidget(payload, userId, roleId); + + if (widgetId) { + await RolesService.addRoleInfo( + roleId, + userId, + 'widgets', + widgetId, + currentUser, + ); + + return res.status(200).send(widgetId); + } else { + return res.status(400).send(widgetId); + } + }), +); + +/** + * @swagger + * /api/openai/ask: + * post: + * security: + * - bearerAuth: [] + * tags: [OpenAI] + * summary: Ask a question to ChatGPT + * description: Send a question to OpenAI's ChatGPT and get a response + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * question: + * type: string + * description: The question to ask ChatGPT + * apiKey: + * type: string + * description: OpenAI API key + * responses: + * 200: + * description: Question successfully answered + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * description: Whether the request was successful + * data: + * type: string + * description: The answer from ChatGPT + * 400: + * description: Invalid request + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 500: + * description: Some server error + */ +router.post( + '/ask-gpt', + wrapAsync(async (req, res) => { + const { prompt } = req.body; + if (!prompt) { + return res.status(400).send({ + success: false, + error: 'Question and API key are required', + }); + } + + const response = await askGpt(prompt); + + if (response.success) { + return res.status(200).send(response); + } else { + return res.status(500).send(response); + } + }), +); + + +module.exports = router; diff --git a/backend/src/routes/pexels.js b/backend/src/routes/pexels.js new file mode 100644 index 0000000..8298595 --- /dev/null +++ b/backend/src/routes/pexels.js @@ -0,0 +1,104 @@ +const express = require('express'); +const router = express.Router(); +const { pexelsKey, pexelsQuery } = require('../config'); +const fetch = require('node-fetch'); + +const KEY = pexelsKey; + +router.get('/image', async (req, res) => { + const headers = { + Authorization: `${KEY}`, + }; + const query = pexelsQuery || 'nature'; + const orientation = 'portrait'; + const perPage = 1; + const url = `https://api.pexels.com/v1/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`; + + try { + const response = await fetch(url, { headers }); + const data = await response.json(); + res.status(200).json(data.photos[0]); + } catch (error) { + res.status(200).json({ error: 'Failed to fetch image' }); + } +}); + +router.get('/video', async (req, res) => { + const headers = { + Authorization: `${KEY}`, + }; + const query = pexelsQuery || 'nature'; + const orientation = 'portrait'; + const perPage = 1; + const url = `https://api.pexels.com/videos/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`; + + try { + const response = await fetch(url, { headers }); + const data = await response.json(); + res.status(200).json(data.videos[0]); + } catch (error) { + res.status(200).json({ error: 'Failed to fetch video' }); + } +}); + +router.get('/multiple-images', async (req, res) => { + const headers = { + Authorization: `${KEY}`, + }; + + const queries = req.query.queries + ? req.query.queries.split(',') + : ['home', 'apple', 'pizza', 'mountains', 'cat']; + const orientation = 'square'; + const perPage = 1; + + const fallbackImage = { + src: 'https://images.pexels.com/photos/8199252/pexels-photo-8199252.jpeg', + photographer: 'Yan Krukau', + photographer_url: 'https://www.pexels.com/@yankrukov', + }; + const fetchFallbackImage = async () => { + try { + const response = await fetch('https://picsum.photos/600'); + return { + src: response.url, + photographer: 'Random Picsum', + photographer_url: 'https://picsum.photos/', + }; + } catch (error) { + return fallbackImage; + } + }; + const fetchImage = async (query) => { + const url = `https://api.pexels.com/v1/search?query=${query}&orientation=${orientation}&per_page=${perPage}&page=1`; + const response = await fetch(url, { headers }); + const data = await response.json(); + return data.photos[0] || null; + }; + + const imagePromises = queries.map((query) => fetchImage(query)); + const imagesResults = await Promise.allSettled(imagePromises); + + const formattedImages = await Promise.all(imagesResults.map(async (result) => { + if (result.status === 'fulfilled' && result.value) { + const image = result.value; + return { + src: image.src?.original || fallbackImage.src, + photographer: image.photographer || fallbackImage.photographer, + photographer_url: image.photographer_url || fallbackImage.photographer_url, + }; + } else { + const fallback = await fetchFallbackImage(); + return { + src: fallback.src || '', + photographer: fallback.photographer || 'Unknown', + photographer_url: fallback.photographer_url || '', + }; + } + })); + + + res.json(formattedImages); +}); + +module.exports = router; diff --git a/backend/src/routes/search.js b/backend/src/routes/search.js new file mode 100644 index 0000000..bbc84fe --- /dev/null +++ b/backend/src/routes/search.js @@ -0,0 +1,47 @@ +const express = require('express'); +const SearchService = require('../services/search'); + +const router = express.Router(); + +/** + * @swagger + * path: + * /api/search: + * post: + * summary: Search + * description: Search results across multiple tables + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * searchQuery: + * type: string + * required: + * - searchQuery + * responses: + * 200: + * description: Successful request + * 400: + * description: Invalid request + * 500: + * description: Internal server error + */ + +router.post('/', async (req, res) => { + const { searchQuery } = req.body; + if (!searchQuery) { + return res.status(400).json({ error: 'Please enter a search query' }); + } + + try { + const foundMatches = await SearchService.search(searchQuery, req.currentUser); + res.json(foundMatches); + } catch (error) { + console.error('Internal Server Error', error); + res.status(500).json({ error: 'Internal Server Error' }); + } + }); + +module.exports = router; diff --git a/backend/src/routes/users.js b/backend/src/routes/users.js new file mode 100644 index 0000000..441441b --- /dev/null +++ b/backend/src/routes/users.js @@ -0,0 +1,421 @@ + +const express = require('express'); + +const UsersService = require('../services/users'); +const UsersDBApi = require('../db/api/users'); +const wrapAsync = require('../helpers').wrapAsync; + +const router = express.Router(); + +const { parse } = require('json2csv'); + +/** + * @swagger + * components: + * schemas: + * Users: + * type: object + * properties: + + * firstName: + * type: string + * default: firstName + * lastName: + * type: string + * default: lastName + * phoneNumber: + * type: string + * default: phoneNumber + * email: + * type: string + * default: email + + */ + +/** + * @swagger + * tags: + * name: Users + * description: The Users managing API + */ + +/** +* @swagger +* /api/users: +* post: +* security: +* - bearerAuth: [] +* tags: [Users] +* summary: Add new item +* description: Add new item +* requestBody: +* required: true +* content: +* application/json: +* schema: +* properties: +* data: +* description: Data of the updated item +* type: object +* $ref: "#/components/schemas/Users" +* responses: +* 200: +* description: The item was successfully added +* content: +* application/json: +* schema: +* $ref: "#/components/schemas/Users" +* 401: +* $ref: "#/components/responses/UnauthorizedError" +* 405: +* description: Invalid input data +* 500: +* description: Some server error +*/ +router.post('/', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await UsersService.create(req.body.data, req.currentUser, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/budgets/bulk-import: + * post: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Bulk import items + * description: Bulk import items + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * data: + * description: Data of the updated items + * type: array + * items: + * $ref: "#/components/schemas/Users" + * responses: + * 200: + * description: The items were successfully imported + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Users" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 405: + * description: Invalid input data + * 500: + * description: Some server error + * + */ +router.post('/bulk-import', wrapAsync(async (req, res) => { + const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; + const link = new URL(referer); + await UsersService.bulkImport(req, res, true, link.host); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/users/{id}: + * put: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Update the data of the selected item + * description: Update the data of the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to update + * required: true + * schema: + * type: string + * requestBody: + * description: Set new item data + * required: true + * content: + * application/json: + * schema: + * properties: + * id: + * description: ID of the updated item + * type: string + * data: + * description: Data of the updated item + * type: object + * $ref: "#/components/schemas/Users" + * required: + * - id + * responses: + * 200: + * description: The item data was successfully updated + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Users" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.put('/:id', wrapAsync(async (req, res) => { + await UsersService.update(req.body.data, req.body.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/users/{id}: + * delete: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Delete the selected item + * description: Delete the selected item + * parameters: + * - in: path + * name: id + * description: Item ID to delete + * required: true + * schema: + * type: string + * responses: + * 200: + * description: The item was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Users" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.delete('/:id', wrapAsync(async (req, res) => { + await UsersService.remove(req.params.id, req.currentUser); + const payload = true; + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/users/deleteByIds: + * post: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Delete the selected item list + * description: Delete the selected item list + * requestBody: + * required: true + * content: + * application/json: + * schema: + * properties: + * ids: + * description: IDs of the updated items + * type: array + * responses: + * 200: + * description: The items was successfully deleted + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Users" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Items not found + * 500: + * description: Some server error + */ +router.post('/deleteByIds', wrapAsync(async (req, res) => { + await UsersService.deleteByIds(req.body.data, req.currentUser); + const payload = true; + res.status(200).send(payload); + })); + +/** + * @swagger + * /api/users: + * get: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Get all users + * description: Get all users + * responses: + * 200: + * description: Users list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Users" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error +*/ +router.get('/', wrapAsync(async (req, res) => { + const filetype = req.query.filetype + const currentUser = req.currentUser; + const payload = await UsersDBApi.findAll( + req.query, { currentUser } + ); + if (filetype && filetype === 'csv') { + const fields = ['id','firstName','lastName','phoneNumber','email', + + ]; + const opts = { fields }; + try { + const csv = parse(payload.rows, opts); + res.status(200).attachment(csv); + res.send(csv) + + } catch (err) { + console.error(err); + } + } else { + res.status(200).send(payload); + } + +})); + +/** + * @swagger + * /api/users/count: + * get: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Count all users + * description: Count all users + * responses: + * 200: + * description: Users count successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Users" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/count', wrapAsync(async (req, res) => { + const currentUser = req.currentUser; + const payload = await UsersDBApi.findAll( + req.query, + null, + { countOnly: true, currentUser } + ); + + res.status(200).send(payload); +})); + +/** + * @swagger + * /api/users/autocomplete: + * get: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Find all users that match search criteria + * description: Find all users that match search criteria + * responses: + * 200: + * description: Users list successfully received + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: "#/components/schemas/Users" + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Data not found + * 500: + * description: Some server error + */ +router.get('/autocomplete', async (req, res) => { + const payload = await UsersDBApi.findAllAutocomplete( + req.query.query, + req.query.limit, + req.query.offset, + ); + + res.status(200).send(payload); +}); + +/** + * @swagger + * /api/users/{id}: + * get: + * security: + * - bearerAuth: [] + * tags: [Users] + * summary: Get selected item + * description: Get selected item + * parameters: + * - in: path + * name: id + * description: ID of item to get + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Selected item successfully received + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Users" + * 400: + * description: Invalid ID supplied + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 404: + * description: Item not found + * 500: + * description: Some server error + */ +router.get('/:id', wrapAsync(async (req, res) => { + const payload = await UsersDBApi.findBy( + { id: req.params.id }, + ); + + delete payload.password; + + res.status(200).send(payload); +})); + +router.use('/', require('../helpers').commonErrorHandler); + +module.exports = router; diff --git a/backend/src/services/auth.js b/backend/src/services/auth.js new file mode 100644 index 0000000..014feae --- /dev/null +++ b/backend/src/services/auth.js @@ -0,0 +1,257 @@ +const UsersDBApi = require('../db/api/users'); +const ValidationError = require('./notifications/errors/validation'); +const ForbiddenError = require('./notifications/errors/forbidden'); +const bcrypt = require('bcrypt'); +const config = require('../config'); +const helpers = require('../helpers'); + +class Auth { + static async signup(email, password, options = {}, host) { + const user = await UsersDBApi.findBy({email}); + + const hashedPassword = await bcrypt.hash( + password, + config.bcrypt.saltRounds, + ); + + if (user) { + if (user.authenticationUid) { + throw new ValidationError( + 'auth.emailAlreadyInUse', + ); + } + + if (user.disabled) { + throw new ValidationError( + 'auth.userDisabled', + ); + } + + await UsersDBApi.updatePassword( + user.id, + hashedPassword, + options, + ); + + // Send Email Address Verification Email + + const data = { + user: { + id: user.id, + email: user.email + } + }; + + return helpers.jwtSign(data); + } + + const newUser = await UsersDBApi.createFromAuth( + { + firstName: email.split('@')[0], + password: hashedPassword, + email: email, + }, + options, + ); + + // Send Email Address Verification Email + + const data = { + user: { + id: newUser.id, + email: newUser.email + } + }; + + return helpers.jwtSign(data); + } + + static async signin(email, password, options = {}) { + const user = await UsersDBApi.findBy({email}); + + if (!user) { + throw new ValidationError( + 'auth.userNotFound', + ); + } + + if (user.disabled) { + throw new ValidationError( + 'auth.userDisabled', + ); + } + + if (!user.password) { + throw new ValidationError( + 'auth.wrongPassword', + ); + } + + // Set emailVerified param + + if (!user.emailVerified) { + throw new ValidationError( + 'auth.userNotVerified', + ); + } + + const passwordsMatch = await bcrypt.compare( + password, + user.password, + ); + + if (!passwordsMatch) { + throw new ValidationError( + 'auth.wrongPassword', + ); + } + + const data = { + user: { + id: user.id, + email: user.email + } + }; + + return helpers.jwtSign(data); + } + + // sendEmailAddressVerificationEmail + + static async sendPasswordResetEmail(email, type = 'register', host) { + let link; + + try { + const token = await UsersDBApi.generatePasswordResetToken( + email, + ); + link = `${host}/password-reset?token=${token}`; + } catch (error) { + console.error(error); + throw new ValidationError( + 'auth.passwordReset.error', + ); + } + + let passwordResetEmail; + if (type === 'register') { + + } + + // password Reset Email + return true; + } + + static async verifyEmail(token, options = {}) { + const user = await UsersDBApi.findByEmailVerificationToken( + token, + options, + ); + + if (!user) { + throw new ValidationError( + 'auth.emailAddressVerificationEmail.invalidToken', + ); + } + + return UsersDBApi.markEmailVerified( + user.id, + options, + ); + } + + static async passwordUpdate(currentPassword, newPassword, options) { + const currentUser = options.currentUser || null; + if (!currentUser) { + throw new ForbiddenError(); + } + + const currentPasswordMatch = await bcrypt.compare( + currentPassword, + currentUser.password, + ); + + if (!currentPasswordMatch) { + throw new ValidationError( + 'auth.wrongPassword' + ) + } + + const newPasswordMatch = await bcrypt.compare( + newPassword, + currentUser.password, + ); + + if (newPasswordMatch) { + throw new ValidationError( + 'auth.passwordUpdate.samePassword' + ) + } + + const hashedPassword = await bcrypt.hash( + newPassword, + config.bcrypt.saltRounds, + ); + + return UsersDBApi.updatePassword( + currentUser.id, + hashedPassword, + options, + ); + } + + static async passwordReset( + token, + password, + options = {}, + ) { + const user = await UsersDBApi.findByPasswordResetToken( + token, + options, + ); + + if (!user) { + throw new ValidationError( + 'auth.passwordReset.invalidToken', + ); + } + + const hashedPassword = await bcrypt.hash( + password, + config.bcrypt.saltRounds, + ); + + return UsersDBApi.updatePassword( + user.id, + hashedPassword, + options, + ); + } + + static async updateProfile(data, currentUser) { + let transaction = await db.sequelize.transaction(); + + try { + await UsersDBApi.findBy( + {id: currentUser.id}, + {transaction}, + ); + + await UsersDBApi.update( + currentUser.id, + data, + { + currentUser, + transaction + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } +} + +module.exports = Auth; diff --git a/backend/src/services/notifications/errors/forbidden.js b/backend/src/services/notifications/errors/forbidden.js new file mode 100644 index 0000000..33e5dc2 --- /dev/null +++ b/backend/src/services/notifications/errors/forbidden.js @@ -0,0 +1,17 @@ +const { getNotification, isNotification } = require('../helpers'); + +module.exports = class ForbiddenError extends Error { + constructor(messageCode) { + let message; + + if (messageCode && isNotification(messageCode)) { + message = getNotification(messageCode); + } + + message = + message || getNotification('errors.forbidden.message'); + + super(message); + this.code = 403; + } +}; diff --git a/backend/src/services/notifications/errors/validation.js b/backend/src/services/notifications/errors/validation.js new file mode 100644 index 0000000..cf3130c --- /dev/null +++ b/backend/src/services/notifications/errors/validation.js @@ -0,0 +1,18 @@ +const { getNotification, isNotification } = require('../helpers'); + +module.exports = class ValidationError extends Error { + constructor(messageCode) { + let message; + + if (messageCode && isNotification(messageCode)) { + message = getNotification(messageCode); + } + + message = + message || + getNotification('errors.validation.message'); + + super(message); + this.code = 400; + } +}; diff --git a/backend/src/services/notifications/helpers.js b/backend/src/services/notifications/helpers.js new file mode 100644 index 0000000..b2f31fd --- /dev/null +++ b/backend/src/services/notifications/helpers.js @@ -0,0 +1,35 @@ +const _get = require('lodash/get'); +const errors = require('./list'); + +function format(message, args) { + if (!message) { + return null; + } + + return message.replace(/{(\d+)}/g, function ( + match, + number, + ) { + return typeof args[number] != 'undefined' + ? args[number] + : match; + }); +} + +const isNotification = (key) => { + const message = _get(errors, key); + return !!message; +}; + +const getNotification = (key, ...args) => { + const message = _get(errors, key); + + if (!message) { + return key; + } + + return format(message, args); +}; + +exports.getNotification = getNotification; +exports.isNotification = isNotification; diff --git a/backend/src/services/notifications/list.js b/backend/src/services/notifications/list.js new file mode 100644 index 0000000..a616552 --- /dev/null +++ b/backend/src/services/notifications/list.js @@ -0,0 +1,104 @@ +const errors = { + app: { + title: 'betterp', + }, + + auth: { + userDisabled: 'Your account is disabled', + forbidden: 'Forbidden', + unauthorized: 'Unauthorized', + userNotFound: `Sorry, we don't recognize your credentials`, + wrongPassword: `Sorry, we don't recognize your credentials`, + weakPassword: 'This password is too weak', + emailAlreadyInUse: 'Email is already in use', + invalidEmail: 'Please provide a valid email', + passwordReset: { + invalidToken: + 'Password reset link is invalid or has expired', + error: `Email not recognized`, + }, + passwordUpdate: { + samePassword: `You can't use the same password. Please create new password` + }, + userNotVerified: `Sorry, your email has not been verified yet`, + emailAddressVerificationEmail: { + invalidToken: + 'Email verification link is invalid or has expired', + error: `Email not recognized`, + }, + }, + + iam: { + errors: { + userAlreadyExists: + 'User with this email already exists', + userNotFound: 'User not found', + disablingHimself: `You can't disable yourself`, + revokingOwnPermission: `You can't revoke your own owner permission`, + deletingHimself: `You can't delete yourself`, + emailRequired: 'Email is required', + }, + }, + + importer: { + errors: { + invalidFileEmpty: 'The file is empty', + invalidFileExcel: + 'Only excel (.xlsx) files are allowed', + invalidFileUpload: + 'Invalid file. Make sure you are using the last version of the template.', + importHashRequired: 'Import hash is required', + importHashExistent: 'Data has already been imported', + userEmailMissing: 'Some items in the CSV do not have an email', + }, + }, + + errors: { + forbidden: { + message: 'Forbidden', + }, + validation: { + message: 'An error occurred', + }, + searchQueryRequired: { + message: 'Search query is required', + }, + }, + + emails: { + invitation: { + subject: `You've been invited to {0}`, + body: ` +

Hello,

+

You've been invited to {0} set password for your {1} account.

+

{2}

+

Thanks,

+

Your {0} team

+ `, + }, + emailAddressVerification: { + subject: `Verify your email for {0}`, + body: ` +

Hello,

+

Follow this link to verify your email address.

+

{0}

+

If you didn't ask to verify this address, you can ignore this email.

+

Thanks,

+

Your {1} team

+ `, + }, + passwordReset: { + subject: `Reset your password for {0}`, + body: ` +

Hello,

+

Follow this link to reset your {0} password for your {1} account.

+

{2}

+

If you didn't ask to reset your password, you can ignore this email.

+

Thanks,

+

Your {0} team

+ `, + }, + }, +}; + +module.exports = errors; diff --git a/backend/src/services/openai.js b/backend/src/services/openai.js new file mode 100644 index 0000000..64ea2ee --- /dev/null +++ b/backend/src/services/openai.js @@ -0,0 +1,51 @@ +const axios = require('axios'); +const { v4: uuid } = require('uuid'); +const RoleService = require('./roles'); +const config = require('../config'); + +module.exports = class OpenAiService { + static async askGpt(prompt) { + if (!config.gpt_key) { + return { + success: false, + error: 'API key is required' + }; + } + try { + const response = await axios.post( + 'https://api.openai.com/v1/chat/completions', + { + model: 'gpt-4o', + messages: [ + { role: 'user', content: prompt } + ] + }, + { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${config.gpt_key}` + } + } + ); + + if (response.status >= 200 && response.status < 300) { + return { + success: true, + data: response.data.choices[0].message.content + }; + } else { + console.error('Error asking question to ChatGPT:', response.data); + return { + success: false, + error: response.data + }; + } + } catch (error) { + console.error('Error asking question to ChatGPT:', error.response?.data || error.message); + return { + success: false, + error: error.response?.data || error.message + }; + } + } +}; diff --git a/backend/src/services/search.js b/backend/src/services/search.js new file mode 100644 index 0000000..7eade19 --- /dev/null +++ b/backend/src/services/search.js @@ -0,0 +1,127 @@ +const db = require('../db/models'); +const ValidationError = require('./notifications/errors/validation'); + +const Sequelize = db.Sequelize; +const Op = Sequelize.Op; + +/** + * @param {string} permission + * @param {object} currentUser + */ +async function checkPermissions(permission, currentUser) { + + if (!currentUser) { + throw new ValidationError('auth.unauthorized'); + } + + const userPermission = currentUser.custom_permissions.find( + (cp) => cp.name === permission, + ); + + if (userPermission) { + return true; + } + + try { + if (!currentUser.app_role) { + throw new ValidationError('auth.forbidden'); + } + + const permissions = await currentUser.app_role.getPermissions(); + + return !!permissions.find((p) => p.name === permission); + } catch (e) { + throw e; + } +} + +module.exports = class SearchService { + static async search(searchQuery, currentUser) { + try { + if (!searchQuery) { + throw new ValidationError('iam.errors.searchQueryRequired'); + } + const tableColumns = { + + "users": [ + + "firstName", + + "lastName", + + "phoneNumber", + + "email", + + ], + + }; + const columnsInt = { + + }; + + let allFoundRecords = []; + + for (const tableName in tableColumns) { + if (tableColumns.hasOwnProperty(tableName)) { + const attributesToSearch = tableColumns[tableName]; + const attributesIntToSearch = columnsInt[tableName] || []; + const whereCondition = { + [Op.or]: [ + ...attributesToSearch.map(attribute => ({ + [attribute]: { + [Op.iLike] : `%${searchQuery}%`, + }, + })), + ...attributesIntToSearch.map(attribute => ( + Sequelize.where( + Sequelize.cast(Sequelize.col(`${tableName}.${attribute}`), 'varchar'), + { [Op.iLike]: `%${searchQuery}%` } + ) + )), + ], + }; + + const hasPermission = await checkPermissions(`READ_${tableName.toUpperCase()}`, currentUser); + if (!hasPermission) { + continue; + } + + const foundRecords = await db[tableName].findAll({ + where: whereCondition, + attributes: [...tableColumns[tableName], 'id', ...attributesIntToSearch], + }); + + const modifiedRecords = foundRecords.map((record) => { + const matchAttribute = []; + + for (const attribute of attributesToSearch) { + if (record[attribute]?.toLowerCase()?.includes(searchQuery.toLowerCase())) { + matchAttribute.push(attribute); + } + } + + for (const attribute of attributesIntToSearch) { + const castedValue = String(record[attribute]); + if (castedValue && castedValue.toLowerCase().includes(searchQuery.toLowerCase())) { + matchAttribute.push(attribute); + } + } + + return { + ...record.get(), + matchAttribute, + tableName, + }; + }); + + allFoundRecords = allFoundRecords.concat(modifiedRecords); + } + } + + return allFoundRecords; + } catch (error) { + throw error; + } + } +} diff --git a/backend/src/services/users.js b/backend/src/services/users.js new file mode 100644 index 0000000..ceca8b8 --- /dev/null +++ b/backend/src/services/users.js @@ -0,0 +1,163 @@ +const db = require('../db/models'); +const UsersDBApi = require('../db/api/users'); +const processFile = require("../middlewares/upload"); +const ValidationError = require('./notifications/errors/validation'); +const csv = require('csv-parser'); +const axios = require('axios'); +const config = require('../config'); +const stream = require('stream'); + +const AuthService = require('./auth'); + +module.exports = class UsersService { + static async create(data, currentUser, sendInvitationEmails = true, host) { + let transaction = await db.sequelize.transaction(); + let email = data.email; + let emailsToInvite = []; + try { + if (email) { + let user = await UsersDBApi.findBy({email}, {transaction}); + if (user) { + throw new ValidationError( + 'iam.errors.userAlreadyExists', + ); + } else { + await UsersDBApi.create( + {data}, + { + currentUser, + transaction, + }, + ); + emailsToInvite.push(email); + } + } else { + throw new ValidationError('iam.errors.emailRequired') + } + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + if (emailsToInvite && emailsToInvite.length) { + if (!sendInvitationEmails) return; + + AuthService.sendPasswordResetEmail(email, 'invitation', host); + } + } + + static async bulkImport(req, res, sendInvitationEmails = true, host) { + const transaction = await db.sequelize.transaction(); + let emailsToInvite = []; + + try { + await processFile(req, res); + const bufferStream = new stream.PassThrough(); + const results = []; + + await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream + + await new Promise((resolve, reject) => { + bufferStream + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', () => { + console.log('results csv', results); + resolve(); + }) + .on('error', (error) => reject(error)); + }); + + const hasAllEmails = results.every((result) => result.email); + + if (!hasAllEmails) { + throw new ValidationError('importer.errors.userEmailMissing'); + } + + await UsersDBApi.bulkImport(results, { + transaction, + ignoreDuplicates: true, + validate: true, + currentUser: req.currentUser + }); + + emailsToInvite = results.map((result) => result.email); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + + if (emailsToInvite && emailsToInvite.length && !sendInvitationEmails) { + + emailsToInvite.forEach((email) => { + AuthService.sendPasswordResetEmail(email, 'invitation', host); + }); + } + } + + static async update(data, id, currentUser) { + const transaction = await db.sequelize.transaction(); + try { + let users = await UsersDBApi.findBy( + {id}, + {transaction}, + ); + + if (!users) { + throw new ValidationError( + 'iam.errors.userNotFound', + ); + } + + const updatedUser = await UsersDBApi.update( + id, + data, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + return updatedUser; + + } catch (error) { + await transaction.rollback(); + throw error; + } + }; + + static async remove(id, currentUser) { + const transaction = await db.sequelize.transaction(); + + try { + if (currentUser.id === id) { + throw new ValidationError( + 'iam.errors.deletingHimself', + ); + } + +// if (currentUser.app_role?.name !== config.roles.admin) { +// throw new ValidationError( +// 'errors.forbidden.message', +// ); +// } + + await UsersDBApi.remove( + id, + { + currentUser, + transaction, + }, + ); + + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + } +}; + diff --git a/backend/watcher.js b/backend/watcher.js new file mode 100644 index 0000000..c9218d5 --- /dev/null +++ b/backend/watcher.js @@ -0,0 +1,45 @@ +const chokidar = require('chokidar'); +const { exec } = require('child_process'); +const nodemon = require('nodemon'); + +const migrationsWatcher = chokidar.watch('./src/db/migrations', { + persistent: true, + ignoreInitial: true +}); +migrationsWatcher.on('add', (filePath) => { + console.log(`[DEBUG] New migration file: ${filePath}`); + exec('npm run db:migrate', (error, stdout, stderr) => { + console.log(stdout); + if (error) { + console.error(stderr); + } + }); +}); + +const seedersWatcher = chokidar.watch('./src/db/seeders', { + persistent: true, + ignoreInitial: true +}); +seedersWatcher.on('add', (filePath) => { + console.log(`[DEBUG] New seed file: ${filePath}`); + exec('npm run db:seed', (error, stdout, stderr) => { + console.log(stdout); + if (error) { + console.error(stderr); + } + }); +}); + +nodemon({ + script: './src/index.js', + ignore: ['./src/db/migrations', './src/db/seeders'], + delay: '500' +}); + +nodemon.on('start', () => { + console.log('Nodemon started'); +}); + +nodemon.on('restart', (files) => { + console.log('Nodemon restarted due changes in:', files); +}); \ No newline at end of file diff --git a/backend/yarn.lock b/backend/yarn.lock new file mode 100644 index 0000000..222a4f9 --- /dev/null +++ b/backend/yarn.lock @@ -0,0 +1,4470 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@apidevtools/json-schema-ref-parser@^9.0.6": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz#8ff5386b365d4c9faa7c8b566ff16a46a577d9b8" + integrity sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + +"@apidevtools/openapi-schemas@^2.0.4": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" + integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== + +"@apidevtools/swagger-methods@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== + +"@apidevtools/swagger-parser@10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz#32057ae99487872c4dd96b314a1ab4b95d89eaf5" + integrity sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.6" + "@apidevtools/openapi-schemas" "^2.0.4" + "@apidevtools/swagger-methods" "^3.0.2" + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + z-schema "^5.0.1" + +"@azure/abort-controller@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz#788ee78457a55af8a1ad342acb182383d2119249" + integrity sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw== + dependencies: + tslib "^2.2.0" + +"@azure/abort-controller@^2.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" + integrity sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA== + dependencies: + tslib "^2.6.2" + +"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0", "@azure/core-auth@^1.5.0": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.7.2.tgz#558b7cb7dd12b00beec07ae5df5907d74df1ebd9" + integrity sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.6.2" + +"@azure/core-client@^1.3.0", "@azure/core-client@^1.5.0", "@azure/core-client@^1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.9.2.tgz#6fc69cee2816883ab6c5cdd653ee4f2ff9774f74" + integrity sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.4.0" + "@azure/core-rest-pipeline" "^1.9.1" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.6.1" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-http-compat@^2.0.1": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz#d1585ada24ba750dc161d816169b33b35f762f0d" + integrity sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-client" "^1.3.0" + "@azure/core-rest-pipeline" "^1.3.0" + +"@azure/core-lro@^2.2.0": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.7.2.tgz#787105027a20e45c77651a98b01a4d3b01b75a08" + integrity sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.2.0" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-paging@^1.1.1": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.6.2.tgz#40d3860dc2df7f291d66350b2cfd9171526433e7" + integrity sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA== + dependencies: + tslib "^2.6.2" + +"@azure/core-rest-pipeline@^1.1.0", "@azure/core-rest-pipeline@^1.3.0", "@azure/core-rest-pipeline@^1.8.1", "@azure/core-rest-pipeline@^1.9.1": + version "1.16.2" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.2.tgz#3f71b09e45a65926cc598478b4f1bcd0fe67bf4b" + integrity sha512-Hnhm/PG9/SQ07JJyLDv3l9Qr8V3xgAe1hFoBYzt6LaalMxfL/ZqFaZf/bz5VN3pMcleCPwl8ivlS2Fjxq/iC8Q== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.4.0" + "@azure/core-tracing" "^1.0.1" + "@azure/core-util" "^1.9.0" + "@azure/logger" "^1.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + tslib "^2.6.2" + +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.1.2.tgz#065dab4e093fb61899988a1cdbc827d9ad90b4ee" + integrity sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA== + dependencies: + tslib "^2.6.2" + +"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0", "@azure/core-util@^1.2.0", "@azure/core-util@^1.3.0", "@azure/core-util@^1.6.1", "@azure/core-util@^1.9.0": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.9.1.tgz#05ea9505c5cdf29c55ccf99a648c66ddd678590b" + integrity sha512-OLsq0etbHO1MA7j6FouXFghuHrAFGk+5C1imcpQ2e+0oZhYF07WLA+NW2Vqs70R7d+zOAWiWM3tbE1sXcDN66g== + dependencies: + "@azure/abort-controller" "^2.0.0" + tslib "^2.6.2" + +"@azure/identity@^4.2.1": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.4.0.tgz#f2743e63d346000a70b0eed5a3b397dedd3984a7" + integrity sha512-oG6oFNMxUuoivYg/ElyZWVSZfw42JQyHbrp+lR7VJ1BYWsGzt34NwyDw3miPp1QI7Qm5+4KAd76wGsbHQmkpkg== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-client" "^1.9.2" + "@azure/core-rest-pipeline" "^1.1.0" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.3.0" + "@azure/logger" "^1.0.0" + "@azure/msal-browser" "^3.14.0" + "@azure/msal-node" "^2.9.2" + events "^3.0.0" + jws "^4.0.0" + open "^8.0.0" + stoppable "^1.1.0" + tslib "^2.2.0" + +"@azure/keyvault-keys@^4.4.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@azure/keyvault-keys/-/keyvault-keys-4.8.0.tgz#1513b3a187bb3a9a372b5980c593962fb793b2ad" + integrity sha512-jkuYxgkw0aaRfk40OQhFqDIupqblIOIlYESWB6DKCVDxQet1pyv86Tfk9M+5uFM0+mCs6+MUHU+Hxh3joiUn4Q== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-client" "^1.5.0" + "@azure/core-http-compat" "^2.0.1" + "@azure/core-lro" "^2.2.0" + "@azure/core-paging" "^1.1.1" + "@azure/core-rest-pipeline" "^1.8.1" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.0.0" + "@azure/logger" "^1.0.0" + tslib "^2.2.0" + +"@azure/logger@^1.0.0": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.1.3.tgz#09a8fd4850b9112865756e92d5e8b728ee457345" + integrity sha512-J8/cIKNQB1Fc9fuYqBVnrppiUtW+5WWJPCj/tAokC5LdSTwkWWttN+jsRgw9BLYD7JDBx7PceiqOBxJJ1tQz3Q== + dependencies: + tslib "^2.6.2" + +"@azure/msal-browser@^3.14.0": + version "3.19.1" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.19.1.tgz#c5e5a7996f95cadc11920bffa2bf6321e3a24555" + integrity sha512-pqYP2gK0GCEa4OxtOqlS+EdFQqhXV6ZuESgSTYWq2ABXyxBVVdd5KNuqgR5SU0OwI2V1YWdFVvLDe1487dyQ0g== + dependencies: + "@azure/msal-common" "14.13.1" + +"@azure/msal-common@14.13.1": + version "14.13.1" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.13.1.tgz#e296cf8cc556082af9c35d803496424e8a95d8b7" + integrity sha512-iUp3BYrsRZ4X3EiaZ2fDjNFjmtYMv9rEQd6c1op6ULn0HWk4ACvDmosL6NaBgWOhl1BAblIbd9vmB5/ilF8d4A== + +"@azure/msal-node@^2.9.2": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.11.1.tgz#7fea67a1c6904301eb8853fae7df86c34306a9cc" + integrity sha512-8ECtug4RL+zsgh20VL8KYHjrRO3MJOeAKEPRXT2lwtiu5U3BdyIdBb50+QZthEkIi60K6pc/pdOx/k5Jp4sLng== + dependencies: + "@azure/msal-common" "14.13.1" + jsonwebtoken "^9.0.0" + uuid "^8.3.0" + +"@google-cloud/paginator@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.7.tgz#fb6f8e24ec841f99defaebf62c75c2e744dd419b" + integrity sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ== + dependencies: + arrify "^2.0.0" + extend "^3.0.2" + +"@google-cloud/projectify@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-2.1.1.tgz#ae6af4fee02d78d044ae434699a630f8df0084ef" + integrity sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ== + +"@google-cloud/promisify@^2.0.0": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.4.tgz#9d8705ecb2baa41b6b2673f3a8e9b7b7e1abc52a" + integrity sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA== + +"@google-cloud/storage@^5.18.2": + version "5.20.5" + resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-5.20.5.tgz#1de71fc88d37934a886bc815722c134b162d335d" + integrity sha512-lOs/dCyveVF8TkVFnFSF7IGd0CJrTm91qiK6JLu+Z8qiT+7Ag0RyVhxZIWkhiACqwABo7kSHDm8FdH8p2wxSSw== + dependencies: + "@google-cloud/paginator" "^3.0.7" + "@google-cloud/projectify" "^2.0.0" + "@google-cloud/promisify" "^2.0.0" + abort-controller "^3.0.0" + arrify "^2.0.0" + async-retry "^1.3.3" + compressible "^2.0.12" + configstore "^5.0.0" + duplexify "^4.0.0" + ent "^2.2.0" + extend "^3.0.2" + gaxios "^4.0.0" + google-auth-library "^7.14.1" + hash-stream-validation "^0.2.2" + mime "^3.0.0" + mime-types "^2.0.8" + p-limit "^3.0.1" + pumpify "^2.0.0" + retry-request "^4.2.2" + stream-events "^1.0.4" + teeny-request "^7.1.3" + uuid "^8.0.0" + xdg-basedir "^4.0.0" + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@js-joda/core@^5.6.1": + version "5.6.3" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-5.6.3.tgz#41ae1c07de1ebe0f6dde1abcbc9700a09b9c6056" + integrity sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA== + +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@mapbox/node-pre-gyp@^1.0.11": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + 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" + +"@one-ini/wasm@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" + integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@types/debug@^4.1.8": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/json-schema@^7.0.6": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/node@*", "@types/node@>=18": + version "20.14.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.11.tgz#09b300423343460455043ddd4d0ded6ac579b74b" + integrity sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA== + dependencies: + undici-types "~5.26.4" + +"@types/readable-stream@^4.0.0": + version "4.0.15" + resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-4.0.15.tgz#e6ec26fe5b02f578c60baf1fa9452e90957d2bfb" + integrity sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw== + dependencies: + "@types/node" "*" + safe-buffer "~5.1.1" + +"@types/validator@^13.7.17": + version "13.12.0" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.12.0.tgz#1fe4c3ae9de5cf5193ce64717c99ef2fa7d8756f" + integrity sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +accepts@^1.3.7, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.1, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array.prototype.map@^1.0.1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.7.tgz#82fa4d6027272d1fca28a63bbda424d0185d78a7" + integrity sha512-XpcFfLoBEAhezrrNw1V+yLXkE7M6uR7xJEsxbG6c/V9v043qurwVJB9r9UTnoSioFDoz1i1VOydpWGmJpfVZbg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-array-method-boxes-properly "^1.0.0" + es-object-atoms "^1.0.0" + is-string "^1.0.7" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +arrify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +async-retry@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== + dependencies: + retry "0.13.1" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axios@^1.6.7: + version "1.7.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" + integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.0, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base64url@3.x.x: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +bcrypt@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2" + integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.11" + node-addon-api "^5.0.0" + +bignumber.js@^9.0.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bl@^6.0.11: + version "6.0.14" + resolved "https://registry.yarnpkg.com/bl/-/bl-6.0.14.tgz#b9ae9862118a3d2ebec999c5318466012314f96c" + integrity sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ== + dependencies: + "@types/readable-stream" "^4.0.0" + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^4.2.0" + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + 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.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-writer@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" + integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + integrity sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg== + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + 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" + +call-me-maybe@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" + integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^3.2.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + 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" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-color@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.4.tgz#d658080290968816b322248b7306fad2346fb2c8" + integrity sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA== + dependencies: + d "^1.0.1" + es5-ext "^0.10.64" + es6-iterator "^2.0.3" + memoizee "^0.4.15" + timers-ext "^0.1.7" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" + integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +compressible@^2.0.12: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +configstore@^5.0.0, configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-env@7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^7.0.0, cross-spawn@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +csv-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-3.0.0.tgz#b88a6256d79e090a97a1b56451f9327b01d710e7" + integrity sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ== + dependencies: + minimist "^1.2.0" + +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.1, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +debug@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +denque@^1.4.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" + integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-libc@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + integrity sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg== + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + +diff@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +doctrine@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +dottie@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.6.tgz#34564ebfc6ec5e5772272d466424ad5b696484d4" + integrity sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA== + +duplexer3@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" + integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== + +duplexify@^4.0.0, duplexify@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" + integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.2" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +editorconfig@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-1.0.4.tgz#040c9a8e9a6c5288388b87c2db07028aa89f53a3" + integrity sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q== + dependencies: + "@one-ini/wasm" "0.1.1" + commander "^10.0.0" + minimatch "9.0.1" + semver "^7.5.3" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +ent@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.1.tgz#68dc99a002f115792c26239baedaaea9e70c0ca2" + integrity sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A== + dependencies: + punycode "^1.4.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.0.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.2: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.0.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +express@4.18.2: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +extend@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-text-encoding@^1.0.0: + version "1.0.6" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz#0aa25f7f638222e3396d72bf936afcf1d42d6867" + integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== + dependencies: + is-buffer "~2.0.3" + +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7" + integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formidable@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" + integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2, fresh@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + 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" + +gaxios@^4.0.0: + version "4.3.3" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22" + integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA== + dependencies: + abort-controller "^3.0.0" + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.6.7" + +gcp-metadata@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.3.1.tgz#fb205fe6a90fef2fd9c85e6ba06e5559ee1eefa9" + integrity sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A== + dependencies: + gaxios "^4.0.0" + json-bigint "^1.0.0" + +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + 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" + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +glob-parent@~5.1.0, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^10.3.3: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + 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" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +global-dirs@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" + integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== + dependencies: + ini "1.3.7" + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +google-auth-library@^7.14.1: + version "7.14.1" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.14.1.tgz#e3483034162f24cc71b95c8a55a210008826213c" + integrity sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^4.0.0" + gcp-metadata "^4.2.0" + gtoken "^5.0.4" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-p12-pem@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" + integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg== + dependencies: + node-forge "^1.3.1" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +gtoken@^5.0.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" + integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ== + dependencies: + gaxios "^4.0.0" + google-p12-pem "^3.1.3" + jws "^4.0.0" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.0, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +hash-stream-validation@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" + integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +helmet@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-4.1.1.tgz#751f0e273d809ace9c172073e0003bed27d27a4a" + integrity sha512-Avg4XxSBrehD94mkRwEljnO+6RZx7AGfk8Wa6K1nxaU+hbXlFOhlOIMgPfFqOYQB/dBCsTpootTGuiOG+CHiQA== + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflection@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.13.4.tgz#65aa696c4e2da6225b148d7a154c449366633a32" + integrity sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" + integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== + +ini@^1.3.4, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.4, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.13.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + +is-map@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +iterate-iterator@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" + integrity sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw== + +iterate-value@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +js-beautify@^1.14.5: + version "1.15.1" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.1.tgz#4695afb508c324e1084ee0b952a102023fc65b64" + integrity sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA== + dependencies: + config-chain "^1.1.13" + editorconfig "^1.0.4" + glob "^10.3.3" + js-cookie "^3.0.5" + nopt "^7.2.0" + +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + +js-md4@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/js-md4/-/js-md4-0.3.2.tgz#cd3b3dc045b0c404556c81ddb5756c23e59d7cf5" + integrity sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA== + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== + +json2csv@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/json2csv/-/json2csv-5.0.7.tgz#f3a583c25abd9804be873e495d1e65ad8d1b54ae" + integrity sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA== + dependencies: + commander "^6.1.0" + jsonparse "^1.3.1" + lodash.get "^4.4.2" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsonwebtoken@8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + 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 "^5.6.0" + +jsonwebtoken@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + 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" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lodash@4.17.21, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== + dependencies: + es5-ext "~0.10.2" + +make-dir@^3.0.0, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memoizee@^0.4.15: + version "0.4.17" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.17.tgz#942a5f8acee281fa6fb9c620bddc57e3b7382949" + integrity sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA== + dependencies: + d "^1.0.2" + es5-ext "^0.10.64" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-descriptors@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +methods@^1.1.2, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +"mime-db@>= 1.43.0 < 2": + version "1.53.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + +mime-types@^2.0.8, mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0, mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" + integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mocha@8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.3.tgz#5e93f873e35dfdd69617ea75f9c68c2ca61c2ac5" + integrity sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.4.2" + debug "4.1.1" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.2" + object.assign "4.1.0" + promise.allsettled "1.0.2" + serialize-javascript "4.0.0" + strip-json-comments "3.0.1" + supports-color "7.1.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.0" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.1" + +moment-timezone@^0.5.43: + version "0.5.45" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" + integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== + dependencies: + moment "^2.29.4" + +moment@2.30.1, moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multer@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4.tgz#e2bc6cac0df57a8832b858d7418ccaa8ebaf7d8c" + integrity sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw== + dependencies: + append-field "^1.0.0" + busboy "^0.2.11" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" + +mysql2@2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.2.5.tgz#72624ffb4816f80f96b9c97fedd8c00935f9f340" + integrity sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g== + dependencies: + denque "^1.4.1" + generate-function "^2.3.1" + iconv-lite "^0.6.2" + long "^4.0.0" + lru-cache "^6.0.0" + named-placeholders "^1.1.2" + seq-queue "^0.0.5" + sqlstring "^2.3.2" + +named-placeholders@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351" + integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w== + dependencies: + lru-cache "^7.14.1" + +native-duplexpair@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/native-duplexpair/-/native-duplexpair-1.0.0.tgz#7899078e64bf3c8a3d732601b3d40ff05db58fa0" + integrity sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + +node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-mocks-http@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.9.0.tgz#6000c570fc4b809603782309be81c73a71d85b71" + integrity sha512-ILf7Ws8xyX9Rl2fLZ7xhZBovrRwgaP84M13esndP6V17M/8j25TpwNzb7Im8U9XCo6fRhdwqiQajWXpsas/E6w== + dependencies: + accepts "^1.3.7" + depd "^1.1.0" + fresh "^0.5.2" + merge-descriptors "^1.0.1" + methods "^1.1.2" + mime "^1.3.4" + parseurl "^1.3.3" + range-parser "^1.2.0" + type-is "^1.6.18" + +nodemailer@6.9.9: + version "6.9.9" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.9.tgz#4549bfbf710cc6addec5064dd0f19874d24248d9" + integrity sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA== + +nodemon@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.5.tgz#df67fe1fd1312ddb0c1e393ae2cf55aacdcec2f3" + integrity sha512-6/jqtZvJdk092pVnD2AIH19KQ9GQZAKOZVy/yT1ueL6aoV+Ix7a1lVZStXzvEh0fP4zE41DDWlkVoHjR6WlozA== + dependencies: + chokidar "^3.2.2" + debug "^3.2.6" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.7" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.3" + update-notifier "^4.1.0" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@^7.2.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== + dependencies: + abbrev "^2.0.0" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +oauth@0.10.x: + version "0.10.0" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.10.0.tgz#3551c4c9b95c53ea437e1e21e46b649482339c58" + integrity sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q== + +oauth@0.9.x: + version "0.9.15" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" + integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== + +object-assign@^4, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-keys@^1.0.11, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +on-finished@2.4.1, on-finished@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^8.0.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.1, p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +packet-reader@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" + integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== + +parseurl@^1.3.3, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +passport-google-oauth2@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/passport-google-oauth2/-/passport-google-oauth2-0.2.0.tgz#fc9ea59e7091f02e24fd16d6be9257ea982ebbc3" + integrity sha512-62EdPtbfVdc55nIXi0p1WOa/fFMM8v/M8uQGnbcXA4OexZWCnfsEi3wo2buag+Is5oqpuHzOtI64JpHk0Xi5RQ== + dependencies: + passport-oauth2 "^1.1.2" + +passport-jwt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.1.tgz#c443795eff322c38d173faa0a3c481479646ec3d" + integrity sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ== + dependencies: + jsonwebtoken "^9.0.0" + passport-strategy "^1.0.0" + +passport-microsoft@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/passport-microsoft/-/passport-microsoft-0.1.0.tgz#dc72c1a38b294d74f4dc55fe93f52e25cb9aa5b4" + integrity sha512-0giBDgE1fnR5X84zJZkQ11hnKVrzEgViwRO6RGsormK9zTxFQmN/UHMTDbIpvhk989VqALewB6Pk1R5vNr3GHw== + dependencies: + passport-oauth2 "1.2.0" + pkginfo "0.2.x" + +passport-oauth2@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.2.0.tgz#49613a3eca85c7a1e65bf1019e2b6b80a10c8ac2" + integrity sha512-6128N+n/MOrJdXxdC2q/PVKXtqgihGFIeup+9bsPybAvMPOUKqdGhh9ZIzZF8rFKJOlxUP9fgP3H0JQe18n0rg== + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + +passport-oauth2@^1.1.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.8.0.tgz#55725771d160f09bbb191828d5e3d559eee079c8" + integrity sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA== + dependencies: + base64url "3.x.x" + oauth "0.10.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + utils-merge "1.x.x" + +passport-strategy@1.x.x, passport-strategy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== + +passport@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.7.0.tgz#3688415a59a48cf8068417a8a8092d4492ca3a05" + integrity sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + utils-merge "^1.0.1" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +pause@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" + integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== + +pg-connection-string@^2.4.0, pg-connection-string@^2.6.1: + version "2.6.4" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.4.tgz#f543862adfa49fa4e14bc8a8892d2a84d754246d" + integrity sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA== + +pg-hstore@2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/pg-hstore/-/pg-hstore-2.3.4.tgz#4425e3e2a3e15d2a334c35581186c27cf2e9b8dd" + integrity sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA== + dependencies: + underscore "^1.13.1" + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-pool@^3.2.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.2.tgz#3a592370b8ae3f02a7c8130d245bc02fa2c5f3f2" + integrity sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg== + +pg-protocol@^1.3.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.1.tgz#21333e6d83b01faaebfe7a33a7ad6bfd9ed38cb3" + integrity sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg== + +pg-types@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + 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" + +pg@8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.4.1.tgz#06cfb6208ae787a869b2f0022da11b90d13d933e" + integrity sha512-NRsH0aGMXmX1z8Dd0iaPCxWUw4ffu+lIAmGm+sTCwuDDWkpEgRCAHZYDwqaNhC5hG5DRMOjSUFasMWhvcmLN1A== + dependencies: + buffer-writer "2.0.0" + packet-reader "1.0.0" + pg-connection-string "^2.4.0" + pg-pool "^3.2.1" + pg-protocol "^1.3.0" + pg-types "^2.1.0" + pgpass "1.x" + +pgpass@1.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" + integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== + dependencies: + split2 "^4.1.0" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkginfo@0.2.x: + version "0.2.3" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.2.3.tgz#7239c42a5ef6c30b8f328439d9b9ff71042490f8" + integrity sha512-7W7wTrE/NsY8xv/DTGjwNIyNah81EQH0MWcTzrHL6pOpMocOGZc0Mbdz9aXxSrp+U0mSmkU8jrNCDCfUs3sOBg== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== + +postgres-date@~1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +promise.allsettled@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" + integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== + dependencies: + array.prototype.map "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + iterate-value "^1.0.0" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pstree.remy@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-2.0.1.tgz#abfc7b5a621307c728b551decbbefb51f0e4aa1e" + integrity sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw== + dependencies: + duplexify "^4.1.1" + inherits "^2.0.3" + pump "^3.0.0" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +pupa@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.0, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@1.2.8, rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.2.2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^4.2.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +registry-auth-token@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" + integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== + dependencies: + rc "1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve@^1.22.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== + dependencies: + lowercase-keys "^1.0.0" + +retry-as-promised@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-7.0.4.tgz#9df73adaeea08cb2948b9d34990549dc13d800a2" + integrity sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA== + +retry-request@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.2.2.tgz#b7d82210b6d2651ed249ba3497f07ea602f1a903" + integrity sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg== + dependencies: + debug "^4.1.1" + extend "^3.0.2" + +retry@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@^5.6.0, semver@^5.7.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.5, semver@^7.5.3, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + 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" + +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== + +sequelize-cli@6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-6.6.2.tgz#8d838b25c988cf136914cdc3843e19d88c3dcb67" + integrity sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg== + dependencies: + cli-color "^2.0.3" + fs-extra "^9.1.0" + js-beautify "^1.14.5" + lodash "^4.17.21" + resolve "^1.22.1" + umzug "^2.3.0" + yargs "^16.2.0" + +sequelize-json-schema@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sequelize-json-schema/-/sequelize-json-schema-2.1.1.tgz#a82d3813925e81485d76ce291f4ff5c8cb2ae492" + integrity sha512-yCGaHnmQQeL6MQ/fOxhkR5C2aOGZyTD6OrgjP4yw1rbuujuIUVdzWN3AsC6r6AvlGZ3EUBBbCJHKl8OIFFES4Q== + +sequelize-pool@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-7.1.0.tgz#210b391af4002762f823188fd6ecfc7413020768" + integrity sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg== + +sequelize@6.35.2: + version "6.35.2" + resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-6.35.2.tgz#9276d24055a9a07bd6812c89ab402659f5853e70" + integrity sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg== + dependencies: + "@types/debug" "^4.1.8" + "@types/validator" "^13.7.17" + debug "^4.3.4" + dottie "^2.0.6" + inflection "^1.13.4" + lodash "^4.17.21" + moment "^2.29.4" + moment-timezone "^0.5.43" + pg-connection-string "^2.6.1" + retry-as-promised "^7.0.4" + semver "^7.5.4" + sequelize-pool "^7.1.0" + toposort-class "^1.0.1" + uuid "^8.3.2" + validator "^13.9.0" + wkx "^0.5.0" + +serialize-javascript@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + 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" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +split2@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +sqlite@4.0.15: + version "4.0.15" + resolved "https://registry.yarnpkg.com/sqlite/-/sqlite-4.0.15.tgz#071e0577afb327fbd74a75354ea15964378392e3" + integrity sha512-irPPTrbVoDvwzRGpe0v8vxpNwMl+q0tXQzffQTcCUnaJzQFO0hfLLvFwGDKxd6vYBuvEr3uvPkObVoGOvVsmzA== + +sqlstring@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" + integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + +stream-events@^1.0.4, stream-events@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== + dependencies: + stubs "^3.0.0" + +stream-shift@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" + integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1, string_decoder@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-json-comments@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +stubs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== + +supports-color@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +swagger-jsdoc@^6.2.8: + version "6.2.8" + resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz#6d33d9fb07ff4a7c1564379c52c08989ec7d0256" + integrity sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ== + dependencies: + commander "6.2.0" + doctrine "3.0.0" + glob "7.1.6" + lodash.mergewith "^4.6.2" + swagger-parser "^10.0.3" + yaml "2.0.0-1" + +swagger-parser@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/swagger-parser/-/swagger-parser-10.0.3.tgz#04cb01c18c3ac192b41161c77f81e79309135d03" + integrity sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg== + dependencies: + "@apidevtools/swagger-parser" "10.0.3" + +swagger-ui-dist@>=5.0.0: + version "5.17.14" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz#e2c222e5bf9e15ccf80ec4bc08b4aaac09792fd6" + integrity sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw== + +swagger-ui-express@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz#fb8c1b781d2793a6bd2f8a205a3f4bd6fa020dd8" + integrity sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA== + dependencies: + swagger-ui-dist ">=5.0.0" + +tar@^6.1.11: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + 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" + +tedious@^18.2.4: + version "18.2.4" + resolved "https://registry.yarnpkg.com/tedious/-/tedious-18.2.4.tgz#c33986f2561b4fde92bb9df70f44ae1a14f71b46" + integrity sha512-+6Nzn/aURTQ+8OxLAJ8fKK5Fbb84HRTI3bHiAC3ZzBKrBg9BHtcHxjmlIni5Zn46hzKiZ5WrDMSwDH8oIYjV8w== + dependencies: + "@azure/identity" "^4.2.1" + "@azure/keyvault-keys" "^4.4.0" + "@js-joda/core" "^5.6.1" + "@types/node" ">=18" + bl "^6.0.11" + iconv-lite "^0.6.3" + js-md4 "^0.3.2" + native-duplexpair "^1.0.0" + sprintf-js "^1.1.3" + +teeny-request@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.2.0.tgz#41347ece068f08d741e7b86df38a4498208b2633" + integrity sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw== + dependencies: + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + node-fetch "^2.6.1" + stream-events "^1.0.5" + uuid "^8.0.0" + +term-size@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" + integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== + +timers-ext@^0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.8.tgz#b4e442f10b7624a29dd2aa42c295e257150cf16c" + integrity sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww== + dependencies: + es5-ext "^0.10.64" + next-tick "^1.1.0" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +toposort-class@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" + integrity sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg== + +touch@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +tslib@^2.2.0, tslib@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@^1.6.18, type-is@^1.6.4, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +uid2@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" + integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== + +umzug@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/umzug/-/umzug-2.3.0.tgz#0ef42b62df54e216b05dcaf627830a6a8b84a184" + integrity sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw== + dependencies: + bluebird "^3.7.2" + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undefsafe@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +underscore@^1.13.1: + version "1.13.6" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-notifier@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" + integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== + dependencies: + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== + dependencies: + prepend-http "^2.0.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +validator@^13.7.0, validator@^13.9.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + 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" + +which@2.0.2, which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +wkx@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c" + integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== + dependencies: + "@types/node" "*" + +workerpool@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" + integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@2.0.0-1: + version "2.0.0-1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-1.tgz#8c3029b3ee2028306d5bcf396980623115ff8d18" + integrity sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^15.0.1: + version "15.0.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.3.tgz#316e263d5febe8b38eef61ac092b33dfcc9b1115" + integrity sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.1.tgz#bd4b0ee05b4c94d058929c32cb09e3fce71d3c5f" + integrity sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA== + dependencies: + camelcase "^5.3.1" + decamelize "^1.2.0" + flat "^4.1.0" + is-plain-obj "^1.1.0" + yargs "^14.2.3" + +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@^14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.1" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + 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" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +z-schema@^5.0.1: + version "5.0.6" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5" + integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^10.0.0" diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 0000000..906ffa4 --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,14 @@ +steps: + - name: 'gcr.io/cloud-builders/docker' + entrypoint: 'bash' + args: ['-c', 'docker pull gcr.io/fldemo-315215/betterp-34374-dev:latest || exit 0'] + - name: 'gcr.io/cloud-builders/docker' + args: [ + 'build', + '-t', 'gcr.io/fldemo-315215/betterp-34374-dev:latest', + '--file', 'Dockerfile.dev', + '--cache-from', 'gcr.io/fldemo-315215/betterp-34374-dev:latest', + '.' + ] +images: ['gcr.io/fldemo-315215/betterp-34374-dev:latest'] +logsBucket: 'gs://fldemo-315215-cloudbuild-logs' \ No newline at end of file diff --git a/community-template.png b/community-template.png new file mode 100644 index 0000000000000000000000000000000000000000..e653a2c6c5ad775cd2a5f4ed0ae8a9fd2cf9dfe2 GIT binary patch literal 87219 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 literal 0 HcmV?d00001 diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 0000000..adbb97d --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1 @@ +data/ \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..69d1021 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,46 @@ +## Description: + + The project contains the **docker folder** and the `Dockerfile`. + + The `Dockerfile` is used to Deploy the project to Google Cloud. + + The **docker folder** contains a couple of helper scripts: + +- `docker-compose.yml` (all our services: web, backend, db are described here) +- `start-backend.sh` (starts backend, but only after the database) +- `wait-for-it.sh` (imported from https://github.com/vishnubob/wait-for-it) + + > To avoid breaking the application, we recommend you don't edit the following files: everything that includes the **docker folder** and `Dokerfile`. + + + ## Run services: + + 1. Install docker compose (https://docs.docker.com/compose/install/) + + 2. Move to `docker` folder. All next steps should be done from this folder. + + ``` cd docker ``` + + 3. Make executables from `wait-for-it.sh` and `start-backend.sh`: + + ``` chmod +x start-backend.sh && chmod +x wait-for-it.sh ``` + + 4. Download dependend projects for services. + + 5. Review the docker-compose.yml file. Make sure that all services have Dockerfiles. Only db service doesn't require a Dockerfile. + + 6. Make sure you have needed ports (see them in `ports`) available on your local machine. + + 7. Start services: + + 7.1. With an empty database `rm -rf data && docker-compose up` + + 7.2. With a stored (from previus runs) database data `docker-compose up` + + 8. Check http://localhost:3000 + + 9. Stop services: + + 9.1. Just press `Ctr+C` + + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..c5b9f32 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,35 @@ + + +version: "3.9" +services: + web: + image: frontend + build: ../frontend + stdin_open: true # docker run -i + tty: true # docker run -t + ports: + - "3000:3000" + db: + image: postgres + volumes: + - ./data/db:/var/lib/postgresql/data + environment: + - POSTGRES_HOST_AUTH_METHOD=trust + - POSTGRES_DB=db_betterp + ports: + - "5432:5432" + backend: + image: backend + volumes: + - ./wait-for-it.sh:/usr/src/app/wait-for-it.sh + - ./start-backend.sh:/usr/src/app/start-backend.sh + build: ../backend + environment: + - DB_HOST=db + ports: + - "8080:8080" + depends_on: + - "db" + + command: ["bash", "./wait-for-it.sh", "db:5432", "--timeout=0", "--strict", "--", "bash", "./start-backend.sh"] + diff --git a/docker/start-backend.sh b/docker/start-backend.sh new file mode 100644 index 0000000..fb353bf --- /dev/null +++ b/docker/start-backend.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +yarn start diff --git a/docker/wait-for-it.sh b/docker/wait-for-it.sh new file mode 100644 index 0000000..d990e0d --- /dev/null +++ b/docker/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..56e10d0 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,19 @@ +FROM node:20.15.1-alpine + +# Create app directory +WORKDIR /usr/src/app + +# Install app dependencies +# A wildcard is used to ensure both package.json AND package-lock.json are copied +# where available (npm@5+) +COPY package*.json ./ + +RUN yarn install +# If you are building your code for production +# RUN npm ci --only=production + +# Bundle app source +COPY . . + +EXPOSE 3000 +CMD [ "yarn", "dev" ] \ No newline at end of file diff --git a/frontend/LICENSE-justboil b/frontend/LICENSE-justboil new file mode 100644 index 0000000..798238d --- /dev/null +++ b/frontend/LICENSE-justboil @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-current JustBoil.me (https://justboil.me) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..b38ff7e --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,89 @@ +# betterp + +## This project was generated by Flatlogic Platform. +## Install + +`cd` to project's dir and run `npm install` + +### Builds + +Build are handled by Next.js CLI — [Info](https://nextjs.org/docs/api-reference/cli) + +### Hot-reloads for development + +``` +npm run dev +``` + +### Builds and minifies for production + +``` +npm run build +``` + +### Exports build for static hosts + +``` +npm run export +``` + +### Lint + +``` +npm run lint +``` + +### Format with prettier + +``` +npm run format +``` + +## Support +For any additional information please refer to [Flatlogic homepage](https://flatlogic.com). + +## To start the project with Docker: +### Description: + +The project contains the **docker folder** and the `Dockerfile`. + +The `Dockerfile` is used to Deploy the project to Google Cloud. + +The **docker folder** contains a couple of helper scripts: + +- `docker-compose.yml` (all our services: web, backend, db are described here) +- `start-backend.sh` (starts backend, but only after the database) +- `wait-for-it.sh` (imported from https://github.com/vishnubob/wait-for-it) + + > To avoid breaking the application, we recommend you don't edit the following files: everything that includes the **docker folder** and `Dokerfile`. + +### Run services: + +1. Install docker compose (https://docs.docker.com/compose/install/) + +2. Move to `docker` folder. All next steps should be done from this folder. + + ``` cd docker ``` + +3. Make executables from `wait-for-it.sh` and `start-backend.sh`: + + ``` chmod +x start-backend.sh && chmod +x wait-for-it.sh ``` + +4. Download dependend projects for services. + +5. Review the docker-compose.yml file. Make sure that all services have Dockerfiles. Only db service doesn't require a Dockerfile. + +6. Make sure you have needed ports (see them in `ports`) available on your local machine. + +7. Start services: + + 7.1. With an empty database `rm -rf data && docker-compose up` + + 7.2. With a stored (from previus runs) database data `docker-compose up` + +8. Check http://localhost:3000 + +9. Stop services: + + 9.1. Just press `Ctr+C` + diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts new file mode 100644 index 0000000..00a8785 --- /dev/null +++ b/frontend/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs new file mode 100644 index 0000000..c5686db --- /dev/null +++ b/frontend/next.config.mjs @@ -0,0 +1,28 @@ +/** + * @type {import('next').NextConfig} + */ + +const output = process.env.NODE_ENV === 'production' ? 'export' : 'standalone'; + const nextConfig = { +trailingSlash: true, + distDir: 'build', + output, + basePath: "", + devIndicators: { + position: 'bottom-left', + }, + typescript: { + ignoreBuildErrors: true, + }, + images: { + unoptimized: true, + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + ], + }, +} + +export default nextConfig diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..1accf82 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,67 @@ +{ + "private": true, + "scripts": { + "dev": "cross-env PORT=${FRONT_PORT:-3000} next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint", + "format": "prettier '{components,pages,src,interfaces,hooks}/**/*.{tsx,ts,js}' --write" + }, + "dependencies": { + "@emotion/react": "^11.11.3", + "@emotion/styled": "^11.11.0", + "@mdi/js": "^7.4.47", + "@mui/material": "^6.3.0", + "@mui/x-data-grid": "^6.19.2", + "@reduxjs/toolkit": "^2.1.0", + "@tailwindcss/typography": "^0.5.13", + "@tinymce/tinymce-react": "^4.3.2", + "axios": "^1.8.4", + "chroma-js": "^2.4.2", + "dayjs": "^1.11.10", + "file-saver": "^2.0.5", + "formik": "^2.4.5", + "html2canvas": "^1.4.1", + "intro.js": "^7.2.0", + "intro.js-react": "^1.0.0", + "jsonwebtoken": "^9.0.2", + "jwt-decode": "^3.1.2", + "lodash": "^4.17.21", + "moment": "^2.30.1", + "next": "^15.3.1", + "numeral": "^2.0.6", + "query-string": "^8.1.0", + "react": "^19.0.0", + "react-datepicker": "^4.10.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^19.0.0", + "react-redux": "^8.0.2", + "react-select": "^5.7.0", + "react-select-async-paginate": "^0.7.9", + "react-switch": "^7.0.0", + "react-toastify": "^11.0.2", + "swr": "^1.3.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.7", + "@tailwindcss/line-clamp": "^0.4.4", + "@types/node": "18.7.16", + "@types/numeral": "^2.0.2", + "@types/react-big-calendar": "^1.8.8", + "@types/react-redux": "^7.1.24", + "@typescript-eslint/eslint-plugin": "^5.37.0", + "@typescript-eslint/parser": "^5.37.0", + "autoprefixer": "^10.4.0", + "cross-env": "^7.0.3", + "eslint": "^8.23.1", + "eslint-config-next": "^13.0.4", + "eslint-config-prettier": "^8.5.0", + "postcss": "^8.4.4", + "postcss-import": "^14.1.0", + "prettier": "^3.2.4", + "tailwindcss": "^3.4.1", + "typescript": "^5.4.5" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..36f40f3 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,9 @@ +/* eslint-env node */ +module.exports = { + plugins: { + 'postcss-import': {}, + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + } +} \ No newline at end of file diff --git a/frontend/prettier.config.js b/frontend/prettier.config.js new file mode 100644 index 0000000..ecde36c --- /dev/null +++ b/frontend/prettier.config.js @@ -0,0 +1,13 @@ +module.exports = { + semi: false, + singleQuote: true, + printWidth: 100, + trailingComma: 'es5', + arrowParens: 'always', + tabWidth: 2, + useTabs: false, + quoteProps: 'as-needed', + jsxSingleQuote: false, + bracketSpacing: true, + bracketSameLine: false, +} \ No newline at end of file diff --git a/frontend/public/data-sources/clients.json b/frontend/public/data-sources/clients.json new file mode 100644 index 0000000..73287b0 --- /dev/null +++ b/frontend/public/data-sources/clients.json @@ -0,0 +1 @@ +{"data":[{"id":19,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Howell-Hand.svg","login":"percy64","name":"Howell Hand","company":"Kiehn-Green","city":"Emelyside","progress":70,"created":"Mar 3, 2022","created_mm_dd_yyyy":"03-03-2022"},{"id":11,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Hope-Howe.svg","login":"dare.concepcion","name":"Hope Howe","company":"Nolan Inc","city":"Paristown","progress":68,"created":"Dec 1, 2022","created_mm_dd_yyyy":"12-01-2022"},{"id":32,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Nelson-Jerde.svg","login":"geovanni.kessler","name":"Nelson Jerde","company":"Nitzsche LLC","city":"Jailynbury","progress":49,"created":"May 18, 2022","created_mm_dd_yyyy":"05-18-2022"},{"id":22,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Kim-Weimann.svg","login":"macejkovic.dashawn","name":"Kim Weimann","company":"Brown-Lueilwitz","city":"New Emie","progress":38,"created":"May 4, 2022","created_mm_dd_yyyy":"05-04-2022"},{"id":34,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Justice-OReilly.svg","login":"hilpert.leora","name":"Justice O'Reilly","company":"Lakin-Muller","city":"New Kacie","progress":38,"created":"Mar 27, 2022","created_mm_dd_yyyy":"03-27-2022"},{"id":48,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Adrienne-Mayer-III.svg","login":"ferry.sophia","name":"Adrienne Mayer III","company":"Kozey, McLaughlin and Kuhn","city":"Howardbury","progress":39,"created":"Mar 29, 2022","created_mm_dd_yyyy":"03-29-2022"},{"id":20,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Mr.-Julien-Ebert.svg","login":"gokuneva","name":"Mr. Julien Ebert","company":"Cormier LLC","city":"South Serenaburgh","progress":29,"created":"Jun 25, 2022","created_mm_dd_yyyy":"06-25-2022"},{"id":47,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Lenna-Smitham.svg","login":"paolo.walter","name":"Lenna Smitham","company":"King Inc","city":"McCulloughfort","progress":59,"created":"Oct 8, 2022","created_mm_dd_yyyy":"10-08-2022"},{"id":24,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Travis-Davis.svg","login":"lkessler","name":"Travis Davis","company":"Leannon and Sons","city":"West Frankton","progress":52,"created":"Oct 20, 2022","created_mm_dd_yyyy":"10-20-2022"},{"id":49,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Prof.-Esteban-Steuber.svg","login":"shana.lang","name":"Prof. Esteban Steuber","company":"Langosh-Ernser","city":"East Sedrick","progress":34,"created":"May 16, 2022","created_mm_dd_yyyy":"05-16-2022"},{"id":36,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Russell-Goodwin-V.svg","login":"jewel07","name":"Russell Goodwin V","company":"Nolan-Stracke","city":"Williamsonmouth","progress":55,"created":"Apr 22, 2022","created_mm_dd_yyyy":"04-22-2022"},{"id":33,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Ms.-Cassidy-Wiegand-DVM.svg","login":"burnice.okuneva","name":"Ms. Cassidy Wiegand DVM","company":"Kuhlman-Hahn","city":"New Ruthiehaven","progress":76,"created":"Sep 16, 2022","created_mm_dd_yyyy":"09-16-2022"},{"id":44,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Mr.-Watson-Brakus-PhD.svg","login":"oconnell.juanita","name":"Mr. Watson Brakus PhD","company":"Osinski, Bins and Kuhn","city":"Lake Gloria","progress":58,"created":"Jun 22, 2022","created_mm_dd_yyyy":"06-22-2022"},{"id":46,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Mr.-Garrison-Friesen-V.svg","login":"vgutmann","name":"Mr. Garrison Friesen V","company":"VonRueden, Rippin and Pfeffer","city":"Port Cieloport","progress":39,"created":"Oct 19, 2022","created_mm_dd_yyyy":"10-19-2022"},{"id":14,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Ms.-Sister-Morar.svg","login":"veum.lucio","name":"Ms. Sister Morar","company":"Gusikowski, Altenwerth and Abbott","city":"Lake Macville","progress":34,"created":"Jun 11, 2022","created_mm_dd_yyyy":"06-11-2022"},{"id":40,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Ms.-Laisha-Reinger.svg","login":"edietrich","name":"Ms. Laisha Reinger","company":"Boehm PLC","city":"West Alexiemouth","progress":73,"created":"Nov 2, 2022","created_mm_dd_yyyy":"11-02-2022"},{"id":5,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Cameron-Lind.svg","login":"mose44","name":"Cameron Lind","company":"Tremblay, Padberg and Pouros","city":"Naderview","progress":59,"created":"Sep 14, 2022","created_mm_dd_yyyy":"09-14-2022"},{"id":43,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Sarai-Little.svg","login":"rau.abelardo","name":"Sarai Little","company":"Deckow LLC","city":"Jeanieborough","progress":49,"created":"Jun 13, 2022","created_mm_dd_yyyy":"06-13-2022"},{"id":2,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Shyann-Kautzer.svg","login":"imurazik","name":"Shyann Kautzer","company":"Osinski, Boehm and Kihn","city":"New Alvera","progress":41,"created":"Feb 15, 2022","created_mm_dd_yyyy":"02-15-2022"},{"id":15,"avatar":"https:\/\/avatars.dicebear.com\/v2\/gridy\/Lorna-Christiansen.svg","login":"annalise97","name":"Lorna Christiansen","company":"Altenwerth-Friesen","city":"Port Elbertland","progress":36,"created":"Mar 9, 2022","created_mm_dd_yyyy":"03-09-2022"}]} diff --git a/frontend/public/data-sources/history.json b/frontend/public/data-sources/history.json new file mode 100644 index 0000000..1cc0c82 --- /dev/null +++ b/frontend/public/data-sources/history.json @@ -0,0 +1 @@ +{"data":[{"id":1,"amount":375.53,"account":"45721474","name":"Home Loan Account","date":"3 days ago","type":"deposit","business":"Turcotte"},{"id":2,"amount":470.26,"account":"94486537","name":"Savings Account","date":"3 days ago","type":"payment","business":"Murazik - Graham"},{"id":3,"amount":971.34,"account":"63189893","name":"Checking Account","date":"5 days ago","type":"invoice","business":"Fahey - Keebler"},{"id":4,"amount":374.63,"account":"74828780","name":"Auto Loan Account","date":"7 days ago","type":"withdraw","business":"Collier - Hintz"}]} \ No newline at end of file diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..c8c4e3e --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/colors.ts b/frontend/src/colors.ts new file mode 100644 index 0000000..ad1ff0e --- /dev/null +++ b/frontend/src/colors.ts @@ -0,0 +1,138 @@ +import type { ColorButtonKey } from './interfaces' + +export const gradientBgBase = 'bg-gradient-to-tr' +export const colorBgBase = 'bg-violet-50/50' +export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500` +export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}` +export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`; +export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to-yellow-500` + +export const colorsBgLight = { + white: 'bg-white text-black', + light: 'bg-white text-black text-black dark:bg-dark-900 dark:text-white', + contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', + success: 'bg-emerald-500 border-emerald-500 dark:bg-brand-blue dark:border-brand-blue text-white', + danger: 'bg-red-500 border-red-500 text-white', + warning: 'bg-yellow-500 border-yellow-500 text-white', + info: 'bg-blue-500 border-blue-500 dark:bg-brand-blue dark:border-brand-blue text-white', +} + +export const colorsText = { + white: 'text-black dark:text-slate-100', + light: 'text-gray-700 dark:text-slate-400', + contrast: 'dark:text-white', + success: 'text-emerald-500', + danger: 'text-red-500', + warning: 'text-yellow-500', + info: 'text-blue-500', +}; + +export const colorsOutline = { + white: [colorsText.white, 'border-gray-100'].join(' '), + light: [colorsText.light, 'border-gray-100'].join(' '), + contrast: [colorsText.contrast, 'border-gray-900 dark:border-slate-100'].join(' '), + success: [colorsText.success, 'border-emerald-500'].join(' '), + danger: [colorsText.danger, 'border-red-500'].join(' '), + warning: [colorsText.warning, 'border-yellow-500'].join(' '), + info: [colorsText.info, 'border-blue-500'].join(' '), +}; + +export const getButtonColor = ( + color: ColorButtonKey, + isOutlined: boolean, + hasHover: boolean, + isActive = false +) => { + if (color === 'void') { + return '' + } + + const colors = { + ring: { + white: 'ring-gray-200 dark:ring-gray-500', + whiteDark: 'ring-gray-200 dark:ring-dark-500', + lightDark: 'ring-gray-200 dark:ring-gray-500', + contrast: 'ring-gray-300 dark:ring-gray-400', + success: 'ring-emerald-300 dark:ring-brand-blue', + danger: 'ring-red-300 dark:ring-red-700', + warning: 'ring-yellow-300 dark:ring-yellow-700', + info: 'ring-blue-300 dark:ring-brand-blue', + }, + active: { + white: 'bg-gray-100', + whiteDark: 'bg-gray-100 dark:bg-dark-800', + lightDark: 'bg-gray-200 dark:bg-slate-700', + contrast: 'bg-gray-700 dark:bg-slate-100', + success: 'bg-emerald-700 dark:bg-brand-blue', + danger: 'bg-red-700 dark:bg-red-600', + warning: 'bg-yellow-700 dark:bg-yellow-600', + info: 'bg-blue-700 dark:bg-brand-blue', + }, + bg: { + white: 'bg-white text-black', + whiteDark: 'bg-white text-black dark:bg-dark-900 dark:text-white', + lightDark: 'bg-gray-100 text-black dark:bg-slate-800 dark:text-white', + contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', + success: 'bg-emerald-600 dark:bg-brand-blue text-white', + danger: 'bg-red-600 text-white dark:bg-red-500', + warning: 'bg-yellow-600 dark:bg-yellow-500 text-white', + info: 'bg-blue-600 dark:bg-brand-blue text-white', + }, + bgHover: { + white: 'hover:bg-gray-100', + whiteDark: 'hover:bg-gray-100 hover:dark:bg-dark-800', + lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700', + contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100', + success: + 'hover:bg-emerald-700 hover:border-emerald-700 hover:dark:bg-brand-blue hover:dark:border-brand-blue', + danger: + 'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600', + warning: + 'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600', + info: 'hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-brand-blue/80 hover:dark:border-brand-blue/80', + }, + borders: { + white: 'border-white', + whiteDark: 'border-white dark:border-dark-900', + lightDark: 'border-gray-100 dark:border-slate-800', + contrast: 'border-gray-800 dark:border-white', + success: 'border-emerald-600 dark:border-brand-blue', + danger: 'border-red-600 dark:border-red-500', + warning: 'border-yellow-600 dark:border-yellow-500', + info: 'border-blue-600 border-blue-600 dark:border-brand-blue', + }, + text: { + contrast: 'dark:text-slate-100', + success: 'text-emerald-600 dark:text-brand-blue', + danger: 'text-red-600 dark:text-red-500', + warning: 'text-yellow-600 dark:text-yellow-500', + info: 'text-blue-600 dark:text-brand-blue', + }, + outlineHover: { + contrast: + 'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black', + success: 'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-brand-blue', + danger: + 'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600', + warning: + 'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600', + info: 'hover:bg-blue-600 hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-brand-blue', + }, + } + + const isOutlinedProcessed = isOutlined && ['white', 'whiteDark', 'lightDark'].indexOf(color) < 0 + + const base = [colors.borders[color], colors.ring[color]] + + if (isActive) { + base.push(colors.active[color]) + } else { + base.push(isOutlinedProcessed ? colors.text[color] : colors.bg[color]) + } + + if (hasHover) { + base.push(isOutlinedProcessed ? colors.outlineHover[color] : colors.bgHover[color]) + } + + return base.join(' ') +} diff --git a/frontend/src/components/AsideMenu.tsx b/frontend/src/components/AsideMenu.tsx new file mode 100644 index 0000000..442dfac --- /dev/null +++ b/frontend/src/components/AsideMenu.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import { MenuAsideItem } from '../interfaces' +import AsideMenuLayer from './AsideMenuLayer' +import OverlayLayer from './OverlayLayer' + +type Props = { + menu: MenuAsideItem[] + isAsideMobileExpanded: boolean + isAsideLgActive: boolean + onAsideLgClose: () => void +} + +export default function AsideMenu({ + isAsideMobileExpanded = false, + isAsideLgActive = false, + ...props +}: Props) { + return ( + <> + + {isAsideLgActive && } + + ) +} diff --git a/frontend/src/components/AsideMenuItem.tsx b/frontend/src/components/AsideMenuItem.tsx new file mode 100644 index 0000000..dbb09b2 --- /dev/null +++ b/frontend/src/components/AsideMenuItem.tsx @@ -0,0 +1,102 @@ +import React, { useEffect, useState } from 'react' +import { mdiMinus, mdiPlus } from '@mdi/js' +import BaseIcon from './BaseIcon' +import Link from 'next/link' +import { getButtonColor } from '../colors' +import AsideMenuList from './AsideMenuList' +import { MenuAsideItem } from '../interfaces' +import { useAppSelector } from '../stores/hooks' +import { useRouter } from 'next/router' + +type Props = { + item: MenuAsideItem + isDropdownList?: boolean +} + +const AsideMenuItem = ({ item, isDropdownList = false }: Props) => { + const [isLinkActive, setIsLinkActive] = useState(false) + const [isDropdownActive, setIsDropdownActive] = useState(false) + + const asideMenuItemStyle = useAppSelector((state) => state.style.asideMenuItemStyle) + const asideMenuDropdownStyle = useAppSelector((state) => state.style.asideMenuDropdownStyle) + const asideMenuItemActiveStyle = useAppSelector((state) => state.style.asideMenuItemActiveStyle) + const borders = useAppSelector((state) => state.style.borders); + const activeLinkColor = useAppSelector( + (state) => state.style.activeLinkColor, + ); + const activeClassAddon = !item.color && isLinkActive ? asideMenuItemActiveStyle : '' + + const { asPath, isReady } = useRouter() + + useEffect(() => { + if (item.href && isReady) { + const linkPathName = new URL(item.href, location.href).pathname + '/'; + const activePathname = new URL(asPath, location.href).pathname + + const activeView = activePathname.split('/')[1]; + const linkPathNameView = linkPathName.split('/')[1]; + + setIsLinkActive(linkPathNameView === activeView); + } + }, [item.href, isReady, asPath]) + + const asideMenuItemInnerContents = ( + <> + {item.icon && ( + + )} + + {item.label} + + {item.menu && ( + + )} + + ) + + const componentClass = [ + 'flex cursor-pointer py-1.5 ', + isDropdownList ? 'px-6 text-sm' : '', + item.color + ? getButtonColor(item.color, false, true) + : `${asideMenuItemStyle}`, + isLinkActive + ? `text-black ${activeLinkColor} dark:text-white dark:bg-dark-800` + : '', + ].join(' '); + + return ( +
  • + {item.withDevider &&
    } + {item.href && ( + + {asideMenuItemInnerContents} + + )} + {!item.href && ( +
    setIsDropdownActive(!isDropdownActive)}> + {asideMenuItemInnerContents} +
    + )} + {item.menu && ( + + )} +
  • + ) +} + +export default AsideMenuItem diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx new file mode 100644 index 0000000..adff7f9 --- /dev/null +++ b/frontend/src/components/AsideMenuLayer.tsx @@ -0,0 +1,58 @@ +import React from 'react' +import { mdiClose } from '@mdi/js' +import BaseIcon from './BaseIcon' +import AsideMenuList from './AsideMenuList' +import { MenuAsideItem } from '../interfaces' +import { useAppSelector } from '../stores/hooks' +import Link from 'next/link'; +import { useAppDispatch } from '../stores/hooks'; +import { createAsyncThunk } from '@reduxjs/toolkit'; +import axios from 'axios'; + +type Props = { + menu: MenuAsideItem[] + className?: string + onAsideLgCloseClick: () => void +} + +export default function AsideMenuLayer({ menu, className = '', ...props }: Props) { + const corners = useAppSelector((state) => state.style.corners); + const asideStyle = useAppSelector((state) => state.style.asideStyle) + const asideBrandStyle = useAppSelector((state) => state.style.asideBrandStyle) + const asideScrollbarsStyle = useAppSelector((state) => state.style.asideScrollbarsStyle) + const darkMode = useAppSelector((state) => state.style.darkMode) + + const handleAsideLgCloseClick = (e: React.MouseEvent) => { + e.preventDefault() + props.onAsideLgCloseClick() + } + + return ( + + ) +} diff --git a/frontend/src/components/AsideMenuList.tsx b/frontend/src/components/AsideMenuList.tsx new file mode 100644 index 0000000..1496110 --- /dev/null +++ b/frontend/src/components/AsideMenuList.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import { MenuAsideItem } from '../interfaces' +import AsideMenuItem from './AsideMenuItem' +import {useAppSelector} from "../stores/hooks"; + +type Props = { + menu: MenuAsideItem[] + isDropdownList?: boolean + className?: string +} + +export default function AsideMenuList({ menu, isDropdownList = false, className = '' }: Props) { + const { currentUser } = useAppSelector((state) => state.auth); + + if (!currentUser) return null; + + return ( +
      + {menu.map((item, index) => { + return ( +
      + +
      + ) + })} +
    + ) +} diff --git a/frontend/src/components/BaseButton.tsx b/frontend/src/components/BaseButton.tsx new file mode 100644 index 0000000..a137dc2 --- /dev/null +++ b/frontend/src/components/BaseButton.tsx @@ -0,0 +1,96 @@ +import React from 'react' +import Link from 'next/link' +import { getButtonColor } from '../colors' +import BaseIcon from './BaseIcon' +import type { ColorButtonKey } from '../interfaces' +import { useAppSelector } from '../stores/hooks'; + +type Props = { + label?: string + icon?: string + iconSize?: string | number + href?: string + target?: string + type?: string + color?: ColorButtonKey + className?: string + iconClassName?: string + asAnchor?: boolean + small?: boolean + outline?: boolean + active?: boolean + disabled?: boolean + roundedFull?: boolean + onClick?: (e: React.MouseEvent) => void +} + +export default function BaseButton({ + label, + icon, + iconSize, + href, + target, + type, + color = 'white', + className = '', + iconClassName = '', + asAnchor = false, + small = false, + outline = false, + active = false, + disabled = false, + roundedFull = false, + onClick, +}: Props) { + const corners = useAppSelector((state) => state.style.corners); + const componentClass = [ + 'inline-flex', + 'justify-center', + 'items-center', + 'whitespace-nowrap', + 'focus:outline-none', + 'transition-colors', + 'focus:ring', + 'duration-150', + 'border', + disabled ? 'cursor-not-allowed' : 'cursor-pointer', + roundedFull ? 'rounded-full' : `${corners}`, + getButtonColor(color, outline, !disabled, active), + className, + ] + + if (!label && icon) { + componentClass.push('p-1') + } else if (small) { + componentClass.push('text-sm', roundedFull ? 'px-3 py-1' : 'p-1') + } else { + componentClass.push('py-2', roundedFull ? 'px-6' : 'px-3') + } + + if (disabled) { + componentClass.push(outline ? 'opacity-50' : 'opacity-70') + } + + const componentClassString = componentClass.join(' ') + + const componentChildren = ( + <> + {icon && } + {label && {label}} + + ) + + if (href && !disabled) { + return ( + + {componentChildren} + + ) + } + + return React.createElement( + asAnchor ? 'a' : 'button', + { className: componentClassString, type: type ?? 'button', target, disabled, onClick }, + componentChildren + ) +} diff --git a/frontend/src/components/BaseButtons.tsx b/frontend/src/components/BaseButtons.tsx new file mode 100644 index 0000000..e81a102 --- /dev/null +++ b/frontend/src/components/BaseButtons.tsx @@ -0,0 +1,38 @@ +import { Children, cloneElement, ReactElement } from 'react'; +import type { ReactNode } from 'react'; + +type Props = { + type?: string; + mb?: string; + noWrap?: boolean; + classAddon?: string; + children?: ReactNode; + className?: string; +}; + +const BaseButtons = ({ + type = 'justify-end', + mb = '-mb-3', + classAddon = 'mr-3 last:mr-0 mb-3', + noWrap = false, + children, + className, + }: Props) => { + return ( +
    + {Children.map(children, (child: ReactElement) => + child + ? cloneElement(child as ReactElement<{ className?: string }>, { + className: `${classAddon} ${(child.props as { className?: string }).className || ''}`, + }) + : null, + )} +
    + ); +}; + +export default BaseButtons; diff --git a/frontend/src/components/BaseDivider.tsx b/frontend/src/components/BaseDivider.tsx new file mode 100644 index 0000000..ac56fa6 --- /dev/null +++ b/frontend/src/components/BaseDivider.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import { useAppSelector } from '../stores/hooks'; +type Props = { + navBar?: boolean +} + +export default function BaseDivider({ navBar = false }: Props) { + const borders = useAppSelector((state) => state.style.borders); + const classAddon = navBar + ? 'hidden lg:block lg:my-0.5 dark:border-dark-700' + : 'my-6 -mx-6 dark:border-dark-800' + + return
    +} diff --git a/frontend/src/components/BaseIcon.tsx b/frontend/src/components/BaseIcon.tsx new file mode 100644 index 0000000..98ec891 --- /dev/null +++ b/frontend/src/components/BaseIcon.tsx @@ -0,0 +1,32 @@ +import React, { ReactNode } from 'react' + +type Props = { + path: string + w?: string + h?: string + fill?: string; + size?: string | number | null + className?: string + children?: ReactNode +} + +export default function BaseIcon({ + path, + fill, + w = 'w-6', + h = 'h-6', + size = null, + className = '', + children, +}: Props) { + const iconSize = size ?? 16 + + return ( + + + + + {children} + + ) +} diff --git a/frontend/src/components/CardBox.tsx b/frontend/src/components/CardBox.tsx new file mode 100644 index 0000000..900386a --- /dev/null +++ b/frontend/src/components/CardBox.tsx @@ -0,0 +1,64 @@ +import React, { ReactNode } from 'react' +import CardBoxComponentBody from './CardBoxComponentBody' +import CardBoxComponentFooter from './CardBoxComponentFooter' +import { useAppSelector } from '../stores/hooks'; + +type Props = { + rounded?: string + flex?: string + className?: string + hasComponentLayout?: boolean + cardBoxClassName?: string + hasTable?: boolean + isHoverable?: boolean + isModal?: boolean + children?: ReactNode + footer?: ReactNode + isList?:boolean + id?: string; + onClick?: (e: React.MouseEvent) => void +} + +export default function CardBox({ + rounded = 'rounded', + flex = 'flex-col', + className = '', + hasComponentLayout = false, + cardBoxClassName = '', + hasTable = false, + isHoverable = false, + isList = false, + isModal = false, + children, + footer, + id ='', + onClick, +}: Props) { + const corners = useAppSelector((state) => state.style.corners); + const cardsStyle = useAppSelector((state) => state.style.cardsStyle); + const componentClass = [ + `flex dark:border-dark-700 dark:bg-dark-900`, + className, + corners !== 'rounded-full'? corners : 'rounded-3xl', + flex, + isList ? '' : `${cardsStyle}`, + hasTable ? '' : `border border-brand-400 dark:border-dark-700` + ] + + if (isHoverable) { + componentClass.push('hover:shadow-lg transition-shadow duration-500') + } + + return React.createElement( + 'div', + { className: componentClass.join(' '), onClick }, + hasComponentLayout ? ( + children + ) : ( + <> + {children} + {footer && {footer}} + + ) + ) +} diff --git a/frontend/src/components/CardBoxComponentBody.tsx b/frontend/src/components/CardBoxComponentBody.tsx new file mode 100644 index 0000000..4c89896 --- /dev/null +++ b/frontend/src/components/CardBoxComponentBody.tsx @@ -0,0 +1,12 @@ +import React, { ReactNode } from 'react' + +type Props = { + noPadding?: boolean + className?: string + children?: ReactNode + id?: string +} + +export default function CardBoxComponentBody({ noPadding = false, className, children, id }: Props) { + return
    {children}
    +} diff --git a/frontend/src/components/CardBoxComponentEmpty.tsx b/frontend/src/components/CardBoxComponentEmpty.tsx new file mode 100644 index 0000000..c71413e --- /dev/null +++ b/frontend/src/components/CardBoxComponentEmpty.tsx @@ -0,0 +1,11 @@ +import React from 'react' + +const CardBoxComponentEmpty = () => { + return ( +
    +

    Nothing's here…

    +
    + ) +} + +export default CardBoxComponentEmpty diff --git a/frontend/src/components/CardBoxComponentFooter.tsx b/frontend/src/components/CardBoxComponentFooter.tsx new file mode 100644 index 0000000..4b41bba --- /dev/null +++ b/frontend/src/components/CardBoxComponentFooter.tsx @@ -0,0 +1,10 @@ +import React, { ReactNode } from 'react' + +type Props = { + className?: string + children?: ReactNode +} + +export default function CardBoxComponentFooter({ className, children }: Props) { + return
    {children}
    +} diff --git a/frontend/src/components/CardBoxComponentTitle.tsx b/frontend/src/components/CardBoxComponentTitle.tsx new file mode 100644 index 0000000..4f92def --- /dev/null +++ b/frontend/src/components/CardBoxComponentTitle.tsx @@ -0,0 +1,17 @@ +import React, { ReactNode } from 'react' + +type Props = { + title: string + children?: ReactNode +} + +const CardBoxComponentTitle = ({ title, children }: Props) => { + return ( +
    +

    {title}

    + {children} +
    + ) +} + +export default CardBoxComponentTitle diff --git a/frontend/src/components/CardBoxModal.tsx b/frontend/src/components/CardBoxModal.tsx new file mode 100644 index 0000000..27d5676 --- /dev/null +++ b/frontend/src/components/CardBoxModal.tsx @@ -0,0 +1,59 @@ +import { mdiClose } from '@mdi/js' +import { ReactNode } from 'react' +import type { ColorButtonKey } from '../interfaces' +import BaseButton from './BaseButton' +import BaseButtons from './BaseButtons' +import CardBox from './CardBox' +import CardBoxComponentTitle from './CardBoxComponentTitle' +import OverlayLayer from './OverlayLayer' + +type Props = { + title: string + buttonColor: ColorButtonKey + buttonLabel: string + isActive: boolean + children?: ReactNode + onConfirm: () => void + onCancel?: () => void +} + +const CardBoxModal = ({ + title, + buttonColor, + buttonLabel, + isActive, + children, + onConfirm, + onCancel, +}: Props) => { + if (!isActive) { + return null + } + + const footer = ( + + + {!!onCancel && } + + ) + + return ( + + + + {!!onCancel && ( + + )} + + +
    {children}
    +
    +
    + ) +} + +export default CardBoxModal diff --git a/frontend/src/components/ChartLineSample/config.ts b/frontend/src/components/ChartLineSample/config.ts new file mode 100644 index 0000000..cacce92 --- /dev/null +++ b/frontend/src/components/ChartLineSample/config.ts @@ -0,0 +1,54 @@ +export const chartColors = { + default: { + primary: '#00D1B2', + info: '#209CEE', + danger: '#FF3860', + }, +} + +const randomChartData = (n: number) => { + const data = [] + + for (let i = 0; i < n; i++) { + data.push(Math.round(Math.random() * 200)) + } + + return data +} + +const datasetObject = (color: string, points: number) => { + return { + fill: false, + borderColor: chartColors.default[color], + borderWidth: 2, + borderDash: [], + borderDashOffset: 0.0, + pointBackgroundColor: chartColors.default[color], + pointBorderColor: 'rgba(255,255,255,0)', + pointHoverBackgroundColor: chartColors.default[color], + pointBorderWidth: 20, + pointHoverRadius: 4, + pointHoverBorderWidth: 15, + pointRadius: 4, + data: randomChartData(points), + tension: 0.5, + cubicInterpolationMode: 'default', + } +} + +export const sampleChartData = (points = 9) => { + const labels = [] + + for (let i = 1; i <= points; i++) { + labels.push(`0${i}`) + } + + return { + labels, + datasets: [ + datasetObject('primary', points), + datasetObject('info', points), + datasetObject('danger', points), + ], + } +} diff --git a/frontend/src/components/ChartLineSample/index.tsx b/frontend/src/components/ChartLineSample/index.tsx new file mode 100644 index 0000000..c7f417b --- /dev/null +++ b/frontend/src/components/ChartLineSample/index.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import { + Chart, + LineElement, + PointElement, + LineController, + LinearScale, + CategoryScale, + Tooltip, +} from 'chart.js' +import { Line } from 'react-chartjs-2' + +Chart.register(LineElement, PointElement, LineController, LinearScale, CategoryScale, Tooltip) + +const options = { + responsive: true, + maintainAspectRatio: false, + scales: { + y: { + display: false, + }, + x: { + display: true, + }, + }, + plugins: { + legend: { + display: false, + }, + }, +} + +const ChartLineSample = ({ data }) => { + return +} + +export default ChartLineSample diff --git a/frontend/src/components/ClickOutside.tsx b/frontend/src/components/ClickOutside.tsx new file mode 100644 index 0000000..1673e29 --- /dev/null +++ b/frontend/src/components/ClickOutside.tsx @@ -0,0 +1,35 @@ +import React, { useCallback, useEffect, useRef, ReactNode, MutableRefObject } from 'react'; + +interface ClickOutsideProps { + children?: ReactNode; + onClickOutside: () => void; + excludedElements: MutableRefObject[]; +} + +const ClickOutside = ({ children, onClickOutside, excludedElements }: ClickOutsideProps) => { + const wrapperRef = useRef(null); + + const handleClickOutside = useCallback( + (event) => { + if ( + wrapperRef.current && + !wrapperRef.current.contains(event.target) && + !excludedElements.some((el) => el.current.contains(event.target)) + ) { + onClickOutside(); + } + }, + [wrapperRef, onClickOutside, ...excludedElements], + ); + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [handleClickOutside]); + + return
    {children}
    ; +}; + +export default ClickOutside; diff --git a/frontend/src/components/DataGridMultiSelect.tsx b/frontend/src/components/DataGridMultiSelect.tsx new file mode 100644 index 0000000..bb82434 --- /dev/null +++ b/frontend/src/components/DataGridMultiSelect.tsx @@ -0,0 +1,55 @@ +import { GridRenderEditCellParams, useGridApiContext } from '@mui/x-data-grid'; +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; +import { MenuItem, Select } from '@mui/material'; + +interface Props { + entityName: string; +} + +const DataGridMultiSelect = (props: GridRenderEditCellParams & Props) => { + const { id, value, field, entityName } = props; + const apiRef = useGridApiContext(); + const [options, setOptions] = useState([]); + + async function callApi(entityName: string) { + const data = await axios(`/${entityName}/autocomplete?limit=100`); + return data.data; + } + + useEffect(() => { + callApi(entityName).then((data) => { + setOptions(data); + }); + }, []); + + const handleChange = (event) => { + const eventValue = event.target.value; // The new value entered by the user + + const newValue = + typeof eventValue === 'string' ? value.split(',') : eventValue; + + apiRef.current.setEditCellValue({ + id, + field, + value: newValue.filter((x) => x !== ''), + }); + }; + + return ( + + ); +}; + +export default DataGridMultiSelect; diff --git a/frontend/src/components/DevModeBadge.tsx b/frontend/src/components/DevModeBadge.tsx new file mode 100644 index 0000000..bc238c4 --- /dev/null +++ b/frontend/src/components/DevModeBadge.tsx @@ -0,0 +1,150 @@ +import React, { useState, useEffect } from 'react'; +import useDevCompilationStatus from '../hooks/useDevCompilationStatus'; +const DevModeBadge: React.FC = () => { + const [isVisible, setIsVisible] = useState(false); + const [isCollapsed, setIsCollapsed] = useState(true); + const compilationStatus = useDevCompilationStatus(); + + const [badgeStyles, setBadgeStyles] = useState({ + position: 'fixed', + bottom: '20px', + left: '70px', + background: 'rgba(0, 0, 0, 0.85)', + color: 'white', + padding: '15px', + borderRadius: '8px', + fontFamily: 'sans-serif', + fontSize: '14px', + lineHeight: '1.5', + textAlign: 'left', + zIndex: 2147483647, + boxShadow: '0 4px 10px rgba(0, 0, 0, 0.3)', + whiteSpace: 'pre-wrap', + transition: 'width 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), padding 0.3s ease-in-out, opacity 0.3s ease-in-out, background-color 0.3s ease-in-out', // Improved transition for width + opacity: 0, + pointerEvents: 'none', + width: '340px', + maxWidth: '340px', + height: 'auto', + overflow: 'hidden', + cursor: 'pointer', + }); + + const fullText = `🚧 Your app is running in development mode. +Current request is compiling and may take a few moments. + +💡 Tip: Set up a stable environment to run your app in production mode—pages will load instantly without compilation delays.`; + + const collapsedText = '🚧 DEV stage'; + + useEffect(() => { + if (compilationStatus === 'ready') { + setIsCollapsed(true); + } else { + setIsCollapsed(false); + } + + }, [compilationStatus]); + + useEffect(() => { + if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') { + setIsVisible(true); + + setBadgeStyles(prev => ({ + ...prev, + opacity: 1, + width: '120px', + maxWidth: '120px', + padding: '6px 10px', + borderRadius: '18px', + whiteSpace: 'nowrap', + fontSize: '12px', + cursor: 'pointer', + pointerEvents: 'auto', + })); + + } else { + setIsVisible(false); + setBadgeStyles(prev => ({ ...prev, opacity: 0 })); + } + }, []); + + useEffect(() => { + if (!isVisible) return; + + if (isCollapsed) { + setBadgeStyles(prev => ({ + ...prev, + width: '140px', + maxWidth: '160px', + padding: '6px 20px', + borderRadius: '18px', + whiteSpace: 'nowrap', + fontSize: '12px', + })); + } else { + setBadgeStyles(prev => ({ + ...prev, + width: '340px', + maxWidth: '340px', + padding: '15px', + borderRadius: '8px', + whiteSpace: 'pre-wrap', + fontSize: '14px', + })); + } + }, [isCollapsed, isVisible]); + + const handleToggleCollapse = (e: React.MouseEvent) => { + e.stopPropagation(); + setIsCollapsed(prev => !prev); + }; + + if (!isVisible) { + return null; + } + + return ( +
    + + + {!isCollapsed && ( +
    + {fullText} +
    + )} + {isCollapsed && ( +
    + {collapsedText} +
    + )} +
    + ); +}; + +export default DevModeBadge; diff --git a/frontend/src/components/DragDropFilePicker.tsx b/frontend/src/components/DragDropFilePicker.tsx new file mode 100644 index 0000000..f6d0ae4 --- /dev/null +++ b/frontend/src/components/DragDropFilePicker.tsx @@ -0,0 +1,124 @@ +import React, { ChangeEvent, useEffect, useState } from 'react'; +import BaseIcon from './BaseIcon'; +import { mdiFileUploadOutline } from '@mdi/js'; + +type Props = { + file: File | null; + setFile: (file: File) => void; + formats?: string; +}; + +const DragDropFilePicker = ({ file, setFile, formats = '' }: Props) => { + const [highlight, setHighlight] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const fileInput = React.createRef(); + + useEffect(() => { + if (!file && fileInput) fileInput.current.value = ''; + }, [file, fileInput]); + + function onFilesAdded(files: FileList | null) { + if (files && files[0]) { + const newFile = files[0]; + const fileExtension = newFile.name.split('.').pop().toLowerCase(); + + if (formats.includes(fileExtension) || !formats) { + setFile(newFile); + setErrorMessage(''); + } else { + setErrorMessage(`Allowed formats: ${formats}`); + } + } + } + + function onDragOver(e) { + e.preventDefault(); + setHighlight(true); + } + + function onDragLeave() { + setHighlight(false); + } + + function onDrop(e) { + e.preventDefault(); + + const files = e.dataTransfer.files; + + onFilesAdded(files); + setHighlight(false); + } + + const onClear = () => { + setFile(null); + setErrorMessage(''); + }; + + return ( +
    + +
    + ); +}; + +export default DragDropFilePicker; diff --git a/frontend/src/components/ErrorBoundary.tsx b/frontend/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..e889c09 --- /dev/null +++ b/frontend/src/components/ErrorBoundary.tsx @@ -0,0 +1,218 @@ +import React, { Component, ErrorInfo, ReactNode } from 'react'; +import { mdiAlertCircle } from '@mdi/js'; +import BaseIcon from './BaseIcon'; + +// Define the props and state interfaces +interface ErrorBoundaryProps { + children: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; + errorInfo: ErrorInfo | null; + showStack: boolean; +} + +// Class-based ErrorBoundary Component +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + + // Define state variables + this.state = { + hasError: false, + error: null, + errorInfo: null, + showStack: false, + }; + } + + static getDerivedStateFromError(error: Error): Partial { + // Update state so the next render will show the fallback UI + return { + hasError: true, + error: error, + }; + } + + componentDidUpdate( + prevProps: Readonly, + prevState: Readonly, + snapshot?: any, + ) { + if (process.env.NODE_ENV !== 'production') { + console.log('componentDidUpdate'); + } + } + + async componentWillUnmount() { + if (process.env.NODE_ENV !== 'production') { + console.log('componentWillUnmount'); + const response = await fetch('/api/logError', { + method: 'DELETE', + }); + + const data = await response.json(); + console.log('Error logs cleared:', data); + } + } + + async componentDidCatch(error: Error, errorInfo: ErrorInfo) { + // Update state with error details (always needed for UI) + this.setState({ + errorInfo: errorInfo, + }); + + // Only perform logging in non-production environments + if (process.env.NODE_ENV !== 'production') { + console.log('Error caught in boundary:', error, errorInfo); + + // Function to log errors to the server + const logErrorToServer = async () => { + try { + const response = await fetch('/api/logError', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + message: error.message, + stack: errorInfo.componentStack, + }), + }); + + const data = await response.json(); + console.log('Error logged:', data); + } catch (err) { + console.error('Failed to log error:', err); + } + }; + + // Function to fetch logged errors (optional) + const fetchLoggedErrors = async () => { + try { + const response = await fetch('/api/logError'); + const data = await response.json(); + console.log('Fetched logs:', data); + } catch (err) { + console.error('Failed to fetch logs:', err); + } + }; + + await logErrorToServer(); + await fetchLoggedErrors(); + } + } + + toggleStack = () => { + this.setState((prevState) => ({ + showStack: !prevState.showStack, + })); + }; + + resetError = () => { + this.setState({ + hasError: false, + error: null, + errorInfo: null, + showStack: false, + }); + }; + + tryAgain = async () => { + // Only clear error logs in non-production environments + if (process.env.NODE_ENV !== 'production') { + try { + const response = await fetch('/api/logError', { + method: 'DELETE', + }); + + const data = await response.json(); + console.log('Error logs cleared:', data); + } catch (e) { + console.error('Failed to clear error logs:', e); + } + } + + // Always reset the error state (needed for UI recovery) + this.setState({ hasError: false }); + }; + + render() { + if (this.state.hasError) { + // Extract error details + const { error, errorInfo, showStack } = this.state; + const errorMessage = error?.message || 'An unexpected error occurred'; + const stackTrace = + errorInfo?.componentStack || error?.stack || 'No stack trace available'; + + return ( +
    +
    +
    +
    + +
    + +
    +

    + Something went wrong +

    +

    + We're sorry, but we encountered an unexpected error. +

    +
    + +
    +

    + {errorMessage} +

    + +
    + + + {showStack && ( +
    +                      {stackTrace}
    +                    
    + )} +
    +
    + +
    + + + +
    +
    +
    +
    + ); + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/frontend/src/components/FooterBar.tsx b/frontend/src/components/FooterBar.tsx new file mode 100644 index 0000000..0acc9c5 --- /dev/null +++ b/frontend/src/components/FooterBar.tsx @@ -0,0 +1,35 @@ +import React, { ReactNode } from 'react' +import { containerMaxW } from '../config' +import Logo from './Logo' + +type Props = { + children?: ReactNode +} + +export default function FooterBar({ children }: Props) { + const year = new Date().getFullYear() + + return ( + + ) +} diff --git a/frontend/src/components/FormCheckRadio.tsx b/frontend/src/components/FormCheckRadio.tsx new file mode 100644 index 0000000..7a9bc31 --- /dev/null +++ b/frontend/src/components/FormCheckRadio.tsx @@ -0,0 +1,20 @@ +import { ReactNode } from 'react' + +type Props = { + children: ReactNode + type: 'checkbox' | 'radio' | 'switch' + label?: string + className?: string +} + +const FormCheckRadio = (props: Props) => { + return ( + + ) +} + +export default FormCheckRadio diff --git a/frontend/src/components/FormCheckRadioGroup.tsx b/frontend/src/components/FormCheckRadioGroup.tsx new file mode 100644 index 0000000..90d98e0 --- /dev/null +++ b/frontend/src/components/FormCheckRadioGroup.tsx @@ -0,0 +1,20 @@ +import { Children, cloneElement, ReactElement, ReactNode } from 'react' + +type Props = { + isColumn?: boolean + children: ReactNode +} + +const FormCheckRadioGroup = (props: Props) => { + return ( +
    + {Children.map(props.children, (child: ReactElement) => + cloneElement(child as ReactElement<{ className?: string }>, { + className: `mr-6 mb-3 last:mr-0 ${(child.props as { className?: string }).className || ''}`, + }), + )} +
    + ) +} + +export default FormCheckRadioGroup diff --git a/frontend/src/components/FormField.tsx b/frontend/src/components/FormField.tsx new file mode 100644 index 0000000..65c8e82 --- /dev/null +++ b/frontend/src/components/FormField.tsx @@ -0,0 +1,80 @@ +import { Children, cloneElement, ReactElement, ReactNode } from 'react' +import BaseIcon from './BaseIcon' +import { useAppSelector } from '../stores/hooks'; + +type Props = { + label?: string + labelFor?: string + help?: string + icons?: string[] | null[] + isBorderless?: boolean + isTransparent?: boolean + hasTextareaHeight?: boolean + children: ReactNode + disabled?: boolean + borderButtom?: boolean + diversity?: boolean + websiteBg?: boolean +} + +const FormField = ({ icons = [], ...props }: Props) => { + const childrenCount = Children.count(props.children) + const bgColor = useAppSelector((state) => state.style.cardsColor); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + const corners = useAppSelector((state) => state.style.corners); + const bgWebsiteColor = useAppSelector((state) => state.style.bgLayoutColor); + let elementWrapperClass = '' + + switch (childrenCount) { + case 2: + elementWrapperClass = 'grid grid-cols-1 gap-3 md:grid-cols-2' + break + case 3: + elementWrapperClass = 'grid grid-cols-1 gap-3 md:grid-cols-3' + } + + const controlClassName = [ + `px-3 py-2 max-w-full border-gray-300 dark:border-dark-700 ${corners} w-full dark:placeholder-gray-400`, + `${focusRing}`, + props.hasTextareaHeight ? 'h-24' : 'h-12', + props.isBorderless ? 'border-0' : 'border', + props.isTransparent ? 'bg-transparent' : `${bgColor} dark:bg-dark-800`, + props.disabled ? 'bg-gray-200 text-gray-100 dark:bg-dark-900 disabled' : '', + props.borderButtom ? `border-0 border-b ${props.diversity ? "border-gray-400" : "placeholder-white border-gray-300/10 border-white"} rounded-none focus:ring-0` : '', + ].join(' '); + + return ( +
    + {props.label && ( + + )} +
    + {Children.map(props.children, (child: ReactElement, index) => ( +
    + {cloneElement(child as ReactElement<{ className?: string }>, { + className: `${controlClassName} ${icons[index] ? 'pl-10' : ''}`, + })} + {icons[index] && ( + + )} +
    + ))} +
    + {props.help && ( +
    {props.help}
    + )} +
    + ) +} + +export default FormField diff --git a/frontend/src/components/FormFilePicker.tsx b/frontend/src/components/FormFilePicker.tsx new file mode 100644 index 0000000..4302edd --- /dev/null +++ b/frontend/src/components/FormFilePicker.tsx @@ -0,0 +1,92 @@ +import {useEffect, useState} from 'react'; +import { ColorButtonKey } from '../interfaces'; +import BaseButton from './BaseButton'; +import FileUploader from "./Uploaders/UploadService"; +import { mdiReload } from '@mdi/js'; +import { useAppSelector } from '../stores/hooks'; + +type Props = { + label?: string; + icon?: string; + accept?: string; + color: ColorButtonKey; + isRoundIcon?: boolean; + path: string; + schema: object; + field: any; + form: any; +}; + +const FormFilePicker = ({ label, icon, accept, color, isRoundIcon, path, + schema, + form, + field, }: Props) => { + const [file, setFile] = useState(null); + const [loading, setLoading] = useState(false); + const corners = useAppSelector((state) => state.style.corners); + const bgColor = useAppSelector((state) => state.style.cardsColor); + let cornersRight; + if (corners === 'rounded'){ + cornersRight = 'rounded-r' + } else if (corners === 'rounded-lg'){ + cornersRight = 'rounded-r-lg' + }else if (corners === 'rounded-full'){ + cornersRight = 'rounded-r-3xl' + }else{ + cornersRight = '' + } + + useEffect(() => { + if (field.value) { + setFile(field.value[0]); + } + }, [field.value]); + const handleFileChange = async (event) => { + const file = event.currentTarget.files[0]; + setFile(file); + + FileUploader.validate(file, schema); + setLoading(true); + const remoteFile = await FileUploader.upload(path, file, schema); + + form.setFieldValue(field.name, [{ ...remoteFile }]); + setLoading(false); + }; + + const showFilename = !isRoundIcon && file; + + return ( +
    + + {showFilename && !loading && ( +
    + + {file.name} + +
    + )} +
    + ); +}; + +export default FormFilePicker; diff --git a/frontend/src/components/FormImagePicker.tsx b/frontend/src/components/FormImagePicker.tsx new file mode 100644 index 0000000..338d2ab --- /dev/null +++ b/frontend/src/components/FormImagePicker.tsx @@ -0,0 +1,90 @@ +import { useState, useEffect } from 'react'; +import { ColorButtonKey } from '../interfaces'; +import BaseButton from './BaseButton'; +import ImagesUploader from "./Uploaders/ImagesUploader"; +import FileUploader from './Uploaders/UploadService'; +import { mdiReload } from '@mdi/js'; +import { useAppSelector } from '../stores/hooks'; + +type Props = { + label?: string; + icon?: string; + accept?: string; + color: ColorButtonKey; + isRoundIcon?: boolean; + path: string; + schema: object; + field: any, + form: any, +}; + +const FormImagePicker = ({ label, icon, accept, color, isRoundIcon, path, schema, form, field }: Props) => { + const [file, setFile] = useState(null); + const [loading, setLoading] = useState(false); + const corners = useAppSelector((state) => state.style.corners); + const bgColor = useAppSelector((state) => state.style.cardsColor); + let cornersRight; + if (corners === 'rounded'){ + cornersRight = 'rounded-r' + } else if (corners === 'rounded-lg'){ + cornersRight = 'rounded-r-lg' + }else if (corners === 'rounded-full'){ + cornersRight = 'rounded-r-3xl' + }else{ + cornersRight = '' + } + + useEffect(() => { + if(field.value) { + setFile(field.value[0]) + } + }, [field.value]) + const handleFileChange = async (event) => { + const file = event.currentTarget.files[0] + setFile(file); + + FileUploader.validate(file, schema); + setLoading(true); + const remoteFile = await FileUploader.upload(path, file, schema); + + form.setFieldValue(field.name, [{...remoteFile}]); + setLoading(false); + }; + + const showFilename = !isRoundIcon && file; + + return ( +
    + + {showFilename && !loading && ( +
    + + {file.name} + +
    + )} +
    + ); +}; + +export default FormImagePicker; diff --git a/frontend/src/components/IconRounded.tsx b/frontend/src/components/IconRounded.tsx new file mode 100644 index 0000000..fc45023 --- /dev/null +++ b/frontend/src/components/IconRounded.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { ColorKey } from '../interfaces' +import { colorsBgLight, colorsText } from '../colors' +import BaseIcon from './BaseIcon' + +type Props = { + icon: string + color: ColorKey + w?: string + h?: string + bg?: boolean + className?: string +} + +export default function IconRounded({ + icon, + color, + w = 'w-12', + h = 'h-12', + bg = false, + className = '', +}: Props) { + const classAddon = bg ? colorsBgLight[color] : `${colorsText[color]} bg-gray-50 dark:bg-slate-800` + + return ( + + ) +} diff --git a/frontend/src/components/ListActionsPopover.tsx b/frontend/src/components/ListActionsPopover.tsx new file mode 100644 index 0000000..e07e678 --- /dev/null +++ b/frontend/src/components/ListActionsPopover.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import Link from 'next/link'; +import Button from '@mui/material/Button'; +import BaseIcon from './BaseIcon'; +import { + mdiDotsVertical, + mdiEye, + mdiPencilOutline, + mdiTrashCan, +} from '@mdi/js'; +import Popover from '@mui/material/Popover'; +import { IconButton } from '@mui/material'; + +type Props = { + itemId: string; + onDelete: (id: string) => void; + hasUpdatePermission: boolean; + className?: string; + iconClassName?: string; + pathEdit: string; + pathView: string; +}; + +const ListActionsPopover = ({ + itemId, + onDelete, + hasUpdatePermission, + className, + iconClassName, + pathEdit, + pathView, + }: Props) => { + const [anchorEl, setAnchorEl] = React.useState(null); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const linkView = pathView; + const linkEdit = pathEdit; + const handleClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + const id = open ? 'simple-popover' : undefined; + + return ( + <> + + + + +
    + + {hasUpdatePermission && ( + + )} + {hasUpdatePermission && ( + + )} +
    +
    + + ); +}; + +export default ListActionsPopover; diff --git a/frontend/src/components/LoadingSpinner.tsx b/frontend/src/components/LoadingSpinner.tsx new file mode 100644 index 0000000..b594b2a --- /dev/null +++ b/frontend/src/components/LoadingSpinner.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +const LoadingSpinner = () => { + return ( +
    +
    +
    +
    +
    +
    + ); +}; + +export default LoadingSpinner; \ No newline at end of file diff --git a/frontend/src/components/Logo/index.tsx b/frontend/src/components/Logo/index.tsx new file mode 100644 index 0000000..a582e29 --- /dev/null +++ b/frontend/src/components/Logo/index.tsx @@ -0,0 +1,15 @@ +import React from 'react' + +type Props = { + className?: string +} + +export default function Logo({ className = '' }: Props) { + return ( + {'Flatlogic + + ) +} diff --git a/frontend/src/components/NavBar.tsx b/frontend/src/components/NavBar.tsx new file mode 100644 index 0000000..1af817f --- /dev/null +++ b/frontend/src/components/NavBar.tsx @@ -0,0 +1,57 @@ +import React, { ReactNode, useState, useEffect } from 'react' +import { mdiClose, mdiDotsVertical } from '@mdi/js' +import { containerMaxW } from '../config' +import BaseIcon from './BaseIcon' +import NavBarItemPlain from './NavBarItemPlain' +import NavBarMenuList from './NavBarMenuList' +import { MenuNavBarItem } from '../interfaces' +import { useAppSelector } from '../stores/hooks'; + +type Props = { + menu: MenuNavBarItem[] + className: string + children: ReactNode +} + +export default function NavBar({ menu, className = '', children }: Props) { + const [isMenuNavBarActive, setIsMenuNavBarActive] = useState(false) + const [isScrolled, setIsScrolled] = useState(false); + const bgColor = useAppSelector((state) => state.style.bgLayoutColor); + + useEffect(() => { + const handleScroll = () => { + const scrolled = window.scrollY > 0; + setIsScrolled(scrolled); + }; + window.addEventListener('scroll', handleScroll); + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, []); + + const handleMenuNavBarToggleClick = () => { + setIsMenuNavBarActive(!isMenuNavBarActive) + } + + return ( + + ) +} diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx new file mode 100644 index 0000000..72935e6 --- /dev/null +++ b/frontend/src/components/NavBarItem.tsx @@ -0,0 +1,132 @@ +import React, {useEffect, useRef} from 'react' +import Link from 'next/link' +import { useState } from 'react' +import { mdiChevronUp, mdiChevronDown } from '@mdi/js' +import BaseDivider from './BaseDivider' +import BaseIcon from './BaseIcon' +import UserAvatarCurrentUser from './UserAvatarCurrentUser' +import NavBarMenuList from './NavBarMenuList' +import { useAppDispatch, useAppSelector } from '../stores/hooks' +import { MenuNavBarItem } from '../interfaces' +import { setDarkMode } from '../stores/styleSlice' +import { logoutUser } from '../stores/authSlice' +import { useRouter } from 'next/router'; +import ClickOutside from "./ClickOutside"; + +type Props = { + item: MenuNavBarItem +} + +export default function NavBarItem({ item }: Props) { + const router = useRouter(); + const dispatch = useAppDispatch(); + const excludedRef = useRef(null); + + const navBarItemLabelActiveColorStyle = useAppSelector( + (state) => state.style.navBarItemLabelActiveColorStyle + ) + const navBarItemLabelStyle = useAppSelector((state) => state.style.navBarItemLabelStyle) + const navBarItemLabelHoverStyle = useAppSelector((state) => state.style.navBarItemLabelHoverStyle) + + const currentUser = useAppSelector((state) => state.auth.currentUser); + + const userName = `${currentUser?.firstName ? currentUser?.firstName : ""} ${currentUser?.lastName ? currentUser?.lastName : ""}`; + + const [isDropdownActive, setIsDropdownActive] = useState(false) + + useEffect(() => { + return () => setIsDropdownActive(false); + }, [router.pathname]); + + const componentClass = [ + 'block lg:flex items-center relative cursor-pointer', + isDropdownActive + ? `${navBarItemLabelActiveColorStyle} dark:text-slate-400` + : `${navBarItemLabelStyle} dark:text-white dark:hover:text-slate-400 ${navBarItemLabelHoverStyle}`, + item.menu ? 'lg:py-2 lg:px-3' : 'py-2 px-3', + item.isDesktopNoLabel ? 'lg:w-16 lg:justify-center' : '', + ].join(' ') + + const itemLabel = item.isCurrentUser ? userName : item.label + + const handleMenuClick = () => { + if (item.menu) { + setIsDropdownActive(!isDropdownActive) + } + + if (item.isToggleLightDark) { + dispatch(setDarkMode(null)) + } + + if(item.isLogout) { + dispatch(logoutUser()) + router.push('/login') + } + } + + const getItemId = (label) => { + switch (label) { + case 'Light/Dark': + return 'themeToggle'; + case 'Log out': + return 'logout'; + default: + return undefined; + } + }; + + const NavBarItemComponentContents = ( + <> +
    + {item.icon && } + + {itemLabel} + + {item.isCurrentUser && } + {item.menu && ( + + )} +
    + {item.menu && ( +
    + setIsDropdownActive(false)} excludedElements={[excludedRef]}> + + +
    + )} + + ) + + if (item.isDivider) { + return + } + + if (item.href) { + return ( + + {NavBarItemComponentContents} + + ) + } + + return
    {NavBarItemComponentContents}
    +} diff --git a/frontend/src/components/NavBarItemPlain.tsx b/frontend/src/components/NavBarItemPlain.tsx new file mode 100644 index 0000000..5589728 --- /dev/null +++ b/frontend/src/components/NavBarItemPlain.tsx @@ -0,0 +1,30 @@ +import React, { ReactNode } from 'react' +import { useAppSelector } from '../stores/hooks' + +type Props = { + display?: string + useMargin?: boolean + children?: ReactNode + onClick?: (e: React.MouseEvent) => void +} + +export default function NavBarItemPlain({ + display = 'flex', + useMargin = false, + onClick, + children, +}: Props) { + const navBarItemLabelStyle = useAppSelector((state) => state.style.navBarItemLabelStyle) + const navBarItemLabelHoverStyle = useAppSelector((state) => state.style.navBarItemLabelHoverStyle) + + const classBase = 'items-center cursor-pointer dark:text-white dark:hover:text-slate-400' + const classAddon = `${display} ${navBarItemLabelStyle} ${navBarItemLabelHoverStyle} ${ + useMargin ? 'my-2 mx-3' : 'py-2 px-3' + }` + + return ( +
    + {children} +
    + ) +} diff --git a/frontend/src/components/NavBarMenuList.tsx b/frontend/src/components/NavBarMenuList.tsx new file mode 100644 index 0000000..0896428 --- /dev/null +++ b/frontend/src/components/NavBarMenuList.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { MenuNavBarItem } from '../interfaces' +import NavBarItem from './NavBarItem' + +type Props = { + menu: MenuNavBarItem[] +} + +export default function NavBarMenuList({ menu }: Props) { + return ( + <> + {menu.map((item, index) => ( +
    + +
    + ))} + + ) +} diff --git a/frontend/src/components/NotificationBar.tsx b/frontend/src/components/NotificationBar.tsx new file mode 100644 index 0000000..a3ade58 --- /dev/null +++ b/frontend/src/components/NotificationBar.tsx @@ -0,0 +1,57 @@ +import { mdiClose } from '@mdi/js' +import React, { ReactNode, useState } from 'react' +import { ColorKey } from '../interfaces' +import { colorsBgLight, colorsOutline } from '../colors' +import BaseButton from './BaseButton' +import BaseIcon from './BaseIcon' + +type Props = { + color: ColorKey + icon?: string + outline?: boolean + children: ReactNode + button?: ReactNode +} + +const NotificationBar = ({ outline = false, children, ...props }: Props) => { + const componentColorClass = outline ? colorsOutline[props.color] : colorsBgLight[props.color] + + const [isDismissed, setIsDismissed] = useState(false) + + const dismiss = (e: React.MouseEvent) => { + e.preventDefault() + + setIsDismissed(true) + } + + if (isDismissed) { + return null + } + + return ( +
    +
    +
    + {props.icon && ( + + )} + {children} +
    + {props.button} + {!props.button && ( + + )} +
    +
    + ) +} + +export default NotificationBar diff --git a/frontend/src/components/OverlayLayer.tsx b/frontend/src/components/OverlayLayer.tsx new file mode 100644 index 0000000..4cb9e6f --- /dev/null +++ b/frontend/src/components/OverlayLayer.tsx @@ -0,0 +1,41 @@ +import React, { ReactNode } from 'react' +import { useAppSelector } from '../stores/hooks' + +type Props = { + zIndex?: string + type?: string + children?: ReactNode + className?: string + onClick: (e: React.MouseEvent) => void +} + +export default function OverlayLayer({ + zIndex = 'z-50', + type = 'flex', + children, + className, + ...props +}: Props) { + const overlayStyle = useAppSelector((state) => state.style.overlayStyle) + + const handleClick = (e: React.MouseEvent) => { + e.preventDefault() + + if (props.onClick) { + props.onClick(e) + } + } + + return ( +
    +
    + + {children} +
    + ) +} diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx new file mode 100644 index 0000000..ec68ef3 --- /dev/null +++ b/frontend/src/components/Pagination.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import { + mdiChevronDoubleLeft, + mdiChevronDoubleRight, + mdiChevronLeft, + mdiChevronRight, +} from '@mdi/js'; +import BaseIcon from './BaseIcon'; + +type Props = { + currentPage: number; + numPages: number; + setCurrentPage: any; +}; + +export const Pagination = ({ + currentPage, + numPages, + setCurrentPage, +}: Props) => { + return ( +
    + {currentPage === 0 && ( +
    + + +
    + )} + {currentPage !== 0 && ( +
    +
    setCurrentPage(0)}> + +
    +
    setCurrentPage(currentPage - 1)}> + +
    +
    + )} +

    + Page {currentPage + 1} of {numPages} +

    + {currentPage !== numPages - 1 && ( +
    +
    setCurrentPage(currentPage + 1)}> + +
    + +
    setCurrentPage(numPages - 1)}> + +
    +
    + )} + {currentPage === numPages - 1 && ( +
    + + +
    + )} +
    + ); +}; diff --git a/frontend/src/components/PasswordSetOrReset.tsx b/frontend/src/components/PasswordSetOrReset.tsx new file mode 100644 index 0000000..07749ba --- /dev/null +++ b/frontend/src/components/PasswordSetOrReset.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import {toast, ToastContainer} from 'react-toastify'; + +import Head from 'next/head'; +import CardBox from '../components/CardBox'; +import SectionFullScreen from '../components/SectionFullScreen'; +import {useRouter} from 'next/router'; +import {getPageTitle} from '../config'; + +import {Field, Form, Formik} from 'formik'; +import FormField from '../components/FormField'; +import BaseButtons from '../components/BaseButtons'; +import BaseButton from '../components/BaseButton'; +import { passwordReset } from '../stores/authSlice'; +import {useAppDispatch} from '../stores/hooks'; + +export default function PasswordSetOrReset() { + const [loading, setLoading] = React.useState(false); + const [isInvitation, setIsInvitation] = React.useState(false); + const router = useRouter(); + const {token, invitation} = router.query; + + const notify = (type, msg) => toast(msg, {type}); + + const dispatch = useAppDispatch(); + + React.useEffect(() => { + if (invitation) { + setIsInvitation(true); + } + }, [invitation]); + + const handleSubmit = async (value) => { + setLoading(true); + if (typeof token === 'string') { + await dispatch( + passwordReset({ + token, + password: value.password, + type: isInvitation && 'invitation', + }), + ); + await router.push('/login'); + } + + setLoading(false); + }; + + return ( + <> + + {isInvitation && {getPageTitle('Set Password')}} + {!isInvitation && {getPageTitle('Reset Password')}} + + + +
    + + {isInvitation &&

    Set Password

    } + {!isInvitation &&

    Reset Password

    } +

    Enter your new password

    + + handleSubmit(values)} + > + {({errors, touched}) => ( +
    + + + + + + + + + + +
    + )} +
    +
    +
    +
    + + + ); +} diff --git a/frontend/src/components/RichTextField.tsx b/frontend/src/components/RichTextField.tsx new file mode 100644 index 0000000..3a1aec8 --- /dev/null +++ b/frontend/src/components/RichTextField.tsx @@ -0,0 +1,42 @@ +import React, { useEffect, useId, useState } from 'react'; +import {Editor} from "@tinymce/tinymce-react"; +import { tinyKey } from '../config' +import {useAppSelector} from "../stores/hooks"; + +export const RichTextField = ({ options, field, form, itemRef, showField }) => { + const [value, setValue] = useState(null); + const darkMode = useAppSelector((state) => state.style.darkMode); + + useEffect(() => { + if (field.value) { + setValue(field.value); + } + }, [field.value]); + + const handleChange = (value) => { + form.setFieldValue(field.name, value); + setValue(value); + }; + + return ( + + ); +}; diff --git a/frontend/src/components/Search.tsx b/frontend/src/components/Search.tsx new file mode 100644 index 0000000..5e7c5de --- /dev/null +++ b/frontend/src/components/Search.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Field, Form, Formik } from 'formik'; +import { useRouter } from 'next/router'; +import { useAppSelector } from '../stores/hooks'; + +const Search = () => { + const router = useRouter(); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + const corners = useAppSelector((state) => state.style.corners); + const cardsStyle = useAppSelector((state) => state.style.cardsStyle); + const validateSearch = (value) => { + let error; + if (!value) { + error = 'Required'; + } else if (value.length < 2) { + error = 'Minimum length: 2 characters' + } + return error; + }; + return ( + { + router.push(`/search?query=${values.search}`); + resetForm(); + setSubmitting(false); + }} + validateOnBlur={false} + validateOnChange={false} + > + {({ errors, touched, values }) => ( +
    + + {errors.search && touched.search && values.search.length < 2 ? ( +
    {errors.search}
    + ) : null} + + )} +
    + ); +}; +export default Search; \ No newline at end of file diff --git a/frontend/src/components/SearchResults.tsx b/frontend/src/components/SearchResults.tsx new file mode 100644 index 0000000..4a417ca --- /dev/null +++ b/frontend/src/components/SearchResults.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import CardBox from './CardBox'; +import { useRouter } from 'next/router'; +import { humanize } from '../helpers/humanize'; + +const SearchResults = ({ searchResults, searchQuery }) => { + const router = useRouter(); + + return ( + <> +

    Matches with: {searchQuery}

    + {Object.keys(searchResults).map((tableName) => ( + <> +

    {humanize(tableName)}

    + +
    + + + + {searchResults[tableName].length > 0 && + Object.keys(searchResults[tableName][0]).map((key) => { + if ( + key !== 'tableName' && + key !== 'id' && + key !== 'matchAttribute' + ) { + return ( + + ); + } + return null; + })} + + + + {searchResults[tableName].map((item, index) => ( + + {Object.keys(item).map((key) => { + if ( + key !== 'tableName' && + key !== 'id' && + key !== 'matchAttribute' + ) { + return ( + + ); + } + return null; + })} + + ))} + +
    + {humanize(key)} +
    + router.push( + `/${tableName}/${tableName}-view/?id=${item['id']}`, + ) + } + > + {item[key]} +
    +
    + {!Object.keys(searchResults).length && ( +
    No data
    + )} +
    + + ))} + {!Object.keys(searchResults).length && ( +
    No matches
    + )} + + ); +}; + +export default SearchResults; diff --git a/frontend/src/components/SectionFullScreen.tsx b/frontend/src/components/SectionFullScreen.tsx new file mode 100644 index 0000000..a570d23 --- /dev/null +++ b/frontend/src/components/SectionFullScreen.tsx @@ -0,0 +1,27 @@ +import React, { ReactNode } from 'react' +import { BgKey } from '../interfaces' +import {gradientBgPurplePink, gradientBgDark, gradientBgPinkRed, gradientBgViolet} from '../colors' +import { useAppSelector } from '../stores/hooks' + +type Props = { + bg: BgKey + children?: ReactNode +} + +export default function SectionFullScreen({ bg, children }: Props) { + const darkMode = useAppSelector((state) => state.style.darkMode) + + let componentClass = 'flex min-h-screen items-center justify-center ' + + if (darkMode) { + componentClass += gradientBgDark + } else if (bg === 'violet') { + componentClass += gradientBgViolet + } else if (bg === 'purplePink') { + componentClass += gradientBgPurplePink + } else if (bg === 'pinkRed') { + componentClass += gradientBgPinkRed + } + + return
    {children}
    +} diff --git a/frontend/src/components/SectionMain.tsx b/frontend/src/components/SectionMain.tsx new file mode 100644 index 0000000..2b18097 --- /dev/null +++ b/frontend/src/components/SectionMain.tsx @@ -0,0 +1,10 @@ +import React, { ReactNode } from 'react' +import { containerMaxW } from '../config' + +type Props = { + children: ReactNode +} + +export default function SectionMain({ children }: Props) { + return
    {children}
    +} diff --git a/frontend/src/components/SectionTitle.tsx b/frontend/src/components/SectionTitle.tsx new file mode 100644 index 0000000..c441e9e --- /dev/null +++ b/frontend/src/components/SectionTitle.tsx @@ -0,0 +1,27 @@ +import React, { ReactNode } from 'react' + +type Props = { + custom?: boolean + first?: boolean + last?: boolean + children: ReactNode +} + +const SectionTitle = ({ custom = false, first = false, last = false, children }: Props) => { + let classAddon = '-my-6' + + if (first) { + classAddon = '-mb-6' + } else if (last) { + classAddon = '-mt-6' + } + + return ( +
    + {custom && children} + {!custom &&

    {children}

    } +
    + ) +} + +export default SectionTitle diff --git a/frontend/src/components/SectionTitleLineWithButton.tsx b/frontend/src/components/SectionTitleLineWithButton.tsx new file mode 100644 index 0000000..3d3c5ef --- /dev/null +++ b/frontend/src/components/SectionTitleLineWithButton.tsx @@ -0,0 +1,29 @@ +import { mdiCog } from '@mdi/js' +import React, { Children, ReactNode } from 'react' +import BaseButton from './BaseButton' +import BaseIcon from './BaseIcon' +import IconRounded from './IconRounded' +import { humanize } from '../helpers/humanize'; + +type Props = { + icon: string + title: string + main?: boolean + children?: ReactNode +} + +export default function SectionTitleLineWithButton({ icon, title, main = false, children }: Props) { + const hasChildren = !!Children.count(children) + + return ( +
    +
    + {icon && main && } + {icon && !main && } +

    {humanize(title)}

    +
    + {children} + {!hasChildren && } +
    + ) +} diff --git a/frontend/src/components/SelectField.tsx b/frontend/src/components/SelectField.tsx new file mode 100644 index 0000000..cab0629 --- /dev/null +++ b/frontend/src/components/SelectField.tsx @@ -0,0 +1,53 @@ +import React, {useEffect, useId, useState} from 'react'; +import { AsyncPaginate } from 'react-select-async-paginate'; +import axios from 'axios' + +export const SelectField = ({ options, field, form, itemRef, showField, disabled }) => { + + const [value, setValue] = useState(null) + const PAGE_SIZE = 100; + + useEffect(() => { + if(options?.id && field?.value?.id) { + setValue({value: field.value?.id, label: field.value[showField]}) + form.setFieldValue(field.name, field.value?.id); + } else if (!field.value) { + setValue(null); + } + }, [options?.id, field?.value?.id, field?.value]) + + const mapResponseToValuesAndLabels = (data) => ({ + value: data.id, + label: data.label, + }) + const handleChange = (option) => { + form.setFieldValue(field.name, option?.value || null) + setValue(option) + } + + async function callApi(inputValue: string, loadedOptions: any[]) { + const path = `/${itemRef}/autocomplete?limit=${PAGE_SIZE}&offset=${loadedOptions.length}${inputValue ? `&query=${inputValue}` : ''}`; + const { data } = await axios(path); + return { + options: data.map(mapResponseToValuesAndLabels), + hasMore: data.length === PAGE_SIZE, + } + } + return ( + 'px-1 py-2', + }} + classNamePrefix='react-select' + instanceId={useId()} + value={value} + debounceTimeout={1000} + loadOptions={callApi} + onChange={handleChange} + defaultOptions + isDisabled={disabled} + isClearable + /> + ) +} + diff --git a/frontend/src/components/SwitchField.tsx b/frontend/src/components/SwitchField.tsx new file mode 100644 index 0000000..a3a66e8 --- /dev/null +++ b/frontend/src/components/SwitchField.tsx @@ -0,0 +1,19 @@ +import React, { useEffect, useId, useState } from 'react'; +import Switch from "react-switch"; + +export const SwitchField = ({ + field, + form, + disabled + }) => { + const handleChange = (data: any) => { + form.setFieldValue( + field.name, + data, + ); + }; + + return ( + + ); +}; diff --git a/frontend/src/components/TableSampleClients.tsx b/frontend/src/components/TableSampleClients.tsx new file mode 100644 index 0000000..3fb3411 --- /dev/null +++ b/frontend/src/components/TableSampleClients.tsx @@ -0,0 +1,141 @@ +import { mdiEye, mdiTrashCan } from '@mdi/js' +import React, { useState } from 'react' +import { useSampleClients } from '../hooks/sampleData' +import { Client } from '../interfaces' +import BaseButton from './BaseButton' +import BaseButtons from './BaseButtons' +import CardBoxModal from './CardBoxModal' +import UserAvatar from './UserAvatar' + +const TableSampleClients = () => { + const { clients } = useSampleClients() + + const perPage = 5 + + const [currentPage, setCurrentPage] = useState(0) + + const clientsPaginated = clients.slice(perPage * currentPage, perPage * (currentPage + 1)) + + const numPages = clients.length / perPage + + const pagesList = [] + + for (let i = 0; i < numPages; i++) { + pagesList.push(i) + } + + const [isModalInfoActive, setIsModalInfoActive] = useState(false) + const [isModalTrashActive, setIsModalTrashActive] = useState(false) + + const handleModalAction = () => { + setIsModalInfoActive(false) + setIsModalTrashActive(false) + } + + return ( + <> + +

    + Lorem ipsum dolor sit amet adipiscing elit +

    +

    This is sample modal

    +
    + + +

    + Lorem ipsum dolor sit amet adipiscing elit +

    +

    This is sample modal

    +
    + + + + + + + + + + + + + {clientsPaginated.map((client: Client) => ( + + + + + + + + + + ))} + +
    + NameCompanyCityProgressCreated +
    + + {client.name}{client.company}{client.city} + + {client.progress} + + + {client.created} + + + setIsModalInfoActive(true)} + small + /> + setIsModalTrashActive(true)} + small + /> + +
    +
    +
    + + {pagesList.map((page) => ( + setCurrentPage(page)} + /> + ))} + + + Page {currentPage + 1} of {numPages} + +
    +
    + + ) +} + +export default TableSampleClients diff --git a/frontend/src/components/Uploaders/FilesUploader.js b/frontend/src/components/Uploaders/FilesUploader.js new file mode 100644 index 0000000..26a4b1a --- /dev/null +++ b/frontend/src/components/Uploaders/FilesUploader.js @@ -0,0 +1,150 @@ +import React, { useState, useRef } from 'react'; +import PropTypes from "prop-types"; +import FileUploader from "components/FormItems/uploaders/UploadService"; +import Errors from "../../../components/FormItems/error/errors"; + +const FilesUploader = (props) => { + const { + value, + onChange, + schema, + path, + max, + readonly + } = props; + + const [loading, setLoading] = useState(false); + const inputElement = useRef(null); + + const valuesArr = () => { + if (!value) { + return []; + } + return Array.isArray(value) ? value : [value]; + }; + + const fileList = () => { + return valuesArr().map((item) => ({ + uid: item.id || undefined, + name: item.name, + status: 'done', + url: item.publicUrl, + })); + }; + + const handleRemove = (id) => { + onChange(valuesArr().filter((item) => item.id !== id)); + }; + + const handleChange = async (event) => { + try { + const files = event.target.files; + + if (!files || !files.length) { + return; + } + + let file = files[0]; + + FileUploader.validate(file, schema); + + setLoading(true) + + file = await FileUploader.upload( + path, + file, + schema, + ); + + inputElement.current.value = ''; + setLoading(false); + onChange([...valuesArr(), file]); + } catch (error) { + inputElement.current.value = ''; + console.log('error', error); + setLoading(false); + Errors.showMessage(error); + } + }; + + const formats = () => { + if (schema && schema.formats) { + return schema.formats + .map((format) => `.${format}`) + .join(','); + } + return undefined; + }; + + const uploadButton = ( + + ); + + return ( +
    + {readonly || (max && fileList().length >= max) + ? null + : uploadButton} + + {valuesArr() && valuesArr().length ? ( +
    + {valuesArr().map((item) => { + return ( +
    + + + {item.name} + + + {!readonly && ( + + )} +
    + ); + })} +
    + ) : null} +
    + ) +} + +FilesUploader.propTypes = { + readonly: PropTypes.bool, + path: PropTypes.string, + max: PropTypes.number, + schema: PropTypes.shape({ + image: PropTypes.bool, + size: PropTypes.number, + formats: PropTypes.arrayOf(PropTypes.string), + }), + value: PropTypes.any, + onChange: PropTypes.func, +}; + +export default FilesUploader; diff --git a/frontend/src/components/Uploaders/ImagesUploader.js b/frontend/src/components/Uploaders/ImagesUploader.js new file mode 100644 index 0000000..725abe7 --- /dev/null +++ b/frontend/src/components/Uploaders/ImagesUploader.js @@ -0,0 +1,237 @@ +import React, { useState, useRef } from 'react'; +import PropTypes from 'prop-types'; +import Button from '@mui/material/Button'; +import CloseIcon from '@mui/icons-material/Close'; +import SearchIcon from '@mui/icons-material/Search'; +import Grid from '@mui/material/Grid'; +import Box from '@mui/material/Box'; +import Dialog from '@mui/material/Dialog'; +import FileUploader from 'components/FormItems/uploaders/UploadService'; +import Errors from '../../../components/FormItems/error/errors'; +import {makeStyles} from "@mui/styles"; + +const useStyles = makeStyles({ + actionButtonsWrapper: { + position: 'relative', + }, + previewContent: { + padding: '0px !important', + }, + imageItem: { + '&.MuiGrid-root': { + margin: 10, + boxShadow: '2px 2px 8px 0 rgb(0 0 0 / 40%)', + borderRadius: 10, + }, + height: '100px', + }, + actionButtons: { + position: 'absolute', + bottom: 5, + right: 4, + }, + previewContainer: { + '& button': { + position: 'absolute', + top: 10, + right: 10, + '& svg': { + height: 50, + width: 50, + fill: '#FFF', + stroke: '#909090', + strokeWidth: 0.5, + }, + }, + }, + button: { + padding: '0px !important', + minWidth: '45px !important', + '& svg': { + height: 36, + width: 36, + fill: '#FFF', + stroke: '#909090', + strokeWidth: 0.5, + }, + } +}); + +const ImagesUploader = (props) => { + const classes = useStyles(); + const { value, onChange, schema, path, max, readonly, name } = props; + + const [loading, setLoading] = useState(false); + const [showPreview, setShowPreview] = useState(false); + const [imageMeta, setImageMeta] = useState({ + imageSrc: null, + imageAlt: null, + }); + const inputElement = useRef(null); + + const valuesArr = () => { + if (!value) { + return []; + } + return Array.isArray(value) ? value : [value]; + }; + + const fileList = () => { + return valuesArr().map((item) => ({ + uid: item.id || undefined, + name: item.name, + status: 'done', + url: item.publicUrl, + })); + }; + + const handleRemove = (id) => { + onChange(valuesArr().filter((item) => item.id !== id)); + }; + + const handleChange = async (event) => { + try { + const files = event.target.files; + + if (!files || !files.length) { + return; + } + + let file = files[0]; + + FileUploader.validate(file, schema); + + setLoading(true); + + file = await FileUploader.upload(path, file, schema); + + inputElement.current.value = ''; + setLoading(false); + onChange([...valuesArr(), file]); + } catch (error) { + inputElement.current.value = ''; + console.log('error', error); + setLoading(false); + Errors.showMessage(error); + } + }; + + const doPreviewImage = (image) => { + setImageMeta({ + imageSrc: image.publicUrl, + imageAlt: image.name, + }); + setShowPreview(true); + }; + + const doCloseImageModal = () => { + setImageMeta({ + imageSrc: null, + imageAlt: null, + }); + setShowPreview(false); + }; + + const uploadButton = ( + + + + ); + + return ( + + {readonly || (max && fileList().length >= max) ? null : uploadButton} + + {valuesArr() && valuesArr().length ? ( + + {valuesArr().map((item) => { + return ( + + {item.name} + +
    +
    + + {!readonly && ( + + )} +
    +
    +
    + ); + })} +
    + ) : null} + + + {imageMeta.imageAlt} + +
    + ); +}; + +ImagesUploader.propTypes = { + readonly: PropTypes.bool, + path: PropTypes.string, + max: PropTypes.number, + schema: PropTypes.shape({ + image: PropTypes.bool, + size: PropTypes.number, + formats: PropTypes.arrayOf(PropTypes.string), + }), + value: PropTypes.any, + onChange: PropTypes.func, + name: PropTypes.string, +}; + +export default ImagesUploader; diff --git a/frontend/src/components/Uploaders/UploadService.js b/frontend/src/components/Uploaders/UploadService.js new file mode 100644 index 0000000..33620a6 --- /dev/null +++ b/frontend/src/components/Uploaders/UploadService.js @@ -0,0 +1,79 @@ +import { v4 as uuidv4 } from 'uuid'; +import Axios from "axios"; +import {baseURLApi} from "../../config"; + +function extractExtensionFrom(filename) { + if (!filename) { + return null; + } + + const regex = /(?:\.([^.]+))?$/; + return regex.exec(filename)[1]; +} + +export default class FileUploader { + static validate(file, schema) { + if (!schema) { + return; + } + + if (schema.image) { + if (!file.type.startsWith("image")) { + throw new Error("You must upload an image"); + } + } + + if (schema.size && file.size > schema.size) { + throw new Error("File is too big."); + } + + const extension = extractExtensionFrom(file.name); + + if (schema.formats && !schema.formats.includes(extension)) { + throw new Error("Invalid format"); + } + } + + static async upload(path, file, schema) { + try { + this.validate(file, schema); + } catch (error) { + return Promise.reject(error); + } + + const extension = extractExtensionFrom(file.name); + const id = uuidv4(); + const filename = `${id}.${extension}`; + const privateUrl = `${path}/${filename}`; + + const publicUrl = await this.uploadToServer(file, path, filename); + + return { + id: id, + name: file.name, + sizeInBytes: file.size, + privateUrl, + publicUrl, + new: true, + }; + } + + static async uploadToServer(file, path, filename) { + const formData = new FormData(); + formData.append("file", file); + formData.append("filename", filename); + const uri = `/file/upload/${path}`; + await Axios.post(uri, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + + const privateUrl = `${path}/${filename}`; + + console.log("process.env.NODE_ENV in uploadToServer function", process.env.NODE_ENV); + console.log("baseURLApi in uploadToServer function", baseURLApi); + + return `${baseURLApi}/file/download?privateUrl=${privateUrl}`; + } +} diff --git a/frontend/src/components/UserAvatar.tsx b/frontend/src/components/UserAvatar.tsx new file mode 100644 index 0000000..257ebdb --- /dev/null +++ b/frontend/src/components/UserAvatar.tsx @@ -0,0 +1,46 @@ +/* eslint-disable @next/next/no-img-element */ +// Why disabled: +// avatars.dicebear.com provides svg avatars +// next/image needs dangerouslyAllowSVG option for that + +import React, { ReactNode } from 'react'; +import BaseIcon from "./BaseIcon"; +import {mdiAccountCircleOutline} from "@mdi/js"; + +type Props = { + username: string; + avatar?: string | null; + image?: object | null; + api?: string; + className?: string; + children?: ReactNode; +}; + +export default function UserAvatar({ + username, + image, + avatar, + className = '', + children, + }: Props) { + + const avatarImage = (image && image[0]) ? `${image[0].publicUrl}` : '#'; + + return ( +
    + {avatarImage === "#" + ? + : {username} + } + {children} +
    + ); +} diff --git a/frontend/src/components/UserAvatarCurrentUser.tsx b/frontend/src/components/UserAvatarCurrentUser.tsx new file mode 100644 index 0000000..7bfc768 --- /dev/null +++ b/frontend/src/components/UserAvatarCurrentUser.tsx @@ -0,0 +1,43 @@ +import React, {ReactNode, useEffect, useState} from 'react'; +import { useAppSelector } from '../stores/hooks'; +import UserAvatar from './UserAvatar'; + +type Props = { + className?: string; + children?: ReactNode; +}; + +export default function UserAvatarCurrentUser({ + className = '', + children, + }: Props) { + const userName = useAppSelector((state) => state.main.userName); + const userAvatar = useAppSelector((state) => state.main.userAvatar); + const { currentUser, isFetching, token } = useAppSelector( + (state) => state.auth, + ); + const { users, loading } = useAppSelector((state) => state.users); + + const [ avatar, setAvatar ] = useState(null) + + useEffect(() => { + currentUserAvatarCheck() + }, []); + + useEffect(() => { + currentUserAvatarCheck() + }, [currentUser?.id, users]); + + const currentUserAvatarCheck = () => { + if (currentUser?.id) { + const image = currentUser?.avatar; + setAvatar(image); + } + } + + return ( + + {children} + + ); +} diff --git a/frontend/src/components/UserCard.tsx b/frontend/src/components/UserCard.tsx new file mode 100644 index 0000000..562db0d --- /dev/null +++ b/frontend/src/components/UserCard.tsx @@ -0,0 +1,48 @@ +import { Field, Form, Formik } from 'formik' +import { useAppSelector } from '../stores/hooks' +import CardBox from './CardBox' +import FormCheckRadio from './FormCheckRadio' +import UserAvatarCurrentUser from './UserAvatarCurrentUser' + +type Props = { + className?: string +} + +const UserCard = ({ className }: Props) => { + const userName = useAppSelector((state) => state.main.userName) + + return ( + +
    + +
    +
    + alert(JSON.stringify(values, null, 2))} + > +
    + + + +
    +
    +
    +

    + Howdy, {userName}! +

    +

    + Last login 12 mins ago from 127.0.0.1 +

    +
    + Verified +
    +
    +
    +
    + ) +} + +export default UserCard diff --git a/frontend/src/components/Users/CardUsers.tsx b/frontend/src/components/Users/CardUsers.tsx new file mode 100644 index 0000000..b98a2e4 --- /dev/null +++ b/frontend/src/components/Users/CardUsers.tsx @@ -0,0 +1,138 @@ +import React from 'react'; +import ListActionsPopover from '../ListActionsPopover'; +import { useAppSelector } from '../../stores/hooks'; +import dataFormatter from '../../helpers/dataFormatter'; +import { Pagination } from '../Pagination'; +import LoadingSpinner from "../LoadingSpinner"; +import Link from 'next/link'; + +type Props = { + users: any[]; + loading: boolean; + onDelete: (id: string) => void; + currentPage: number; + numPages: number; + onPageChange: (page: number) => void; +}; + +const CardUsers = ({ + users, + loading, + onDelete, + currentPage, + numPages, + onPageChange, +}: Props) => { + const asideScrollbarsStyle = useAppSelector( + (state) => state.style.asideScrollbarsStyle, + ); + const bgColor = useAppSelector((state) => state.style.cardsColor); + const darkMode = useAppSelector((state) => state.style.darkMode); + const corners = useAppSelector((state) => state.style.corners); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + + return ( +
    + {loading && } +
      + {!loading && users.map((item, index) => ( +
    • + + {item.firstName} + + +
      + +
      +
    +
    + +
    +
    First Name
    +
    +
    + { item.firstName } +
    +
    +
    + +
    +
    Last Name
    +
    +
    + { item.lastName } +
    +
    +
    + +
    +
    Phone Number
    +
    +
    + { item.phoneNumber } +
    +
    +
    + +
    +
    E-Mail
    +
    +
    + { item.email } +
    +
    +
    + +
    +
    Disabled
    +
    +
    + { dataFormatter.booleanFormatter(item.disabled) } +
    +
    +
    + +
    +
    Avatar
    +
    +
    + { item.avatar } +
    +
    +
    + +
    + + ))} + {!loading && users.length === 0 && ( +
    +

    No data to display

    +
    + )} + +
    + +
    + + ); +}; + +export default CardUsers; diff --git a/frontend/src/components/Users/ListUsers.tsx b/frontend/src/components/Users/ListUsers.tsx new file mode 100644 index 0000000..05ac3f2 --- /dev/null +++ b/frontend/src/components/Users/ListUsers.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import CardBox from '../CardBox'; +import dataFormatter from '../../helpers/dataFormatter'; +import ListActionsPopover from "../ListActionsPopover"; +import {useAppSelector} from "../../stores/hooks"; +import {Pagination} from "../Pagination"; +import LoadingSpinner from "../LoadingSpinner"; +import Link from 'next/link'; + +type Props = { + users: any[]; + loading: boolean; + onDelete: (id: string) => void; + currentPage: number; + numPages: number; + onPageChange: (page: number) => void; +}; + +const ListUsers = ({ users, loading, onDelete, currentPage, numPages, onPageChange }: Props) => { + const corners = useAppSelector((state) => state.style.corners); + const bgColor = useAppSelector((state) => state.style.cardsColor); + + return ( + <> +
    + {loading && } + {!loading && users.map((item) => ( +
    + +
    + + +
    +

    First Name

    +

    { item.firstName }

    +
    + +
    +

    Last Name

    +

    { item.lastName }

    +
    + +
    +

    Phone Number

    +

    { item.phoneNumber }

    +
    + +
    +

    E-Mail

    +

    { item.email }

    +
    + +
    +

    Disabled

    +

    { dataFormatter.booleanFormatter(item.disabled) }

    +
    + +
    +

    Avatar

    +

    { item.avatar }

    +
    + + + +
    +
    +
    + ))} + {!loading && users.length === 0 && ( +
    +

    No data to display

    +
    + )} +
    +
    + +
    + + ) +}; + +export default ListUsers diff --git a/frontend/src/components/Users/TableUsers.tsx b/frontend/src/components/Users/TableUsers.tsx new file mode 100644 index 0000000..0049bbf --- /dev/null +++ b/frontend/src/components/Users/TableUsers.tsx @@ -0,0 +1,441 @@ +import React, { useEffect, useState, useMemo } from 'react' +import { createPortal } from 'react-dom'; +import { ToastContainer, toast } from 'react-toastify'; +import BaseButton from '../BaseButton' +import CardBoxModal from '../CardBoxModal' +import CardBox from "../CardBox"; +import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/users/usersSlice' +import { useAppDispatch, useAppSelector } from '../../stores/hooks' +import { useRouter } from 'next/router' +import { Field, Form, Formik } from "formik"; +import { + DataGrid, + GridColDef, +} from '@mui/x-data-grid'; +import {loadColumns} from "./configureUsersCols"; +import _ from 'lodash'; +import dataFormatter from '../../helpers/dataFormatter' +import {dataGridStyles} from "../../styles"; +import axios from 'axios'; + +const perPage = 10; + +const TableSampleUsers = ({ filterItems, setFilterItems, filters, showGrid }) => { + const notify = (type, msg) => toast( msg, {type, position: "bottom-center"}); + + const dispatch = useAppDispatch(); + const router = useRouter(); + + const pagesList = []; + const [id, setId] = useState(null); + const [currentPage, setCurrentPage] = useState(0); + const [filterRequest, setFilterRequest] = React.useState(''); + const [columns, setColumns] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const [sortModel, setSortModel] = useState([ + { + field: '', + sort: 'desc', + }, + ]); + const { users, loading, count, notify: usersNotify, refetch } = useAppSelector((state) => state.users) + const { currentUser } = useAppSelector((state) => state.auth); + const focusRing = useAppSelector((state) => state.style.focusRingColor); + const bgColor = useAppSelector((state) => state.style.bgLayoutColor); + const corners = useAppSelector((state) => state.style.corners); + const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage); + for (let i = 0; i < numPages; i++) { + pagesList.push(i); + } + + const loadData = async (page = currentPage, request = filterRequest) => { + if (page !== currentPage) setCurrentPage(page); + if (request !== filterRequest) setFilterRequest(request); + const { sort, field } = sortModel[0]; + + const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`; + dispatch(fetch({ limit: perPage, page, query })); + }; + + useEffect(() => { + if (usersNotify.showNotification) { + notify(usersNotify.typeNotification, usersNotify.textNotification); + } + }, [usersNotify.showNotification]); + + useEffect(() => { + if (!currentUser) return; + loadData(); + }, [sortModel, currentUser]); + + useEffect(() => { + if (refetch) { + loadData(0); + dispatch(setRefetch(false)); + } + }, [refetch, dispatch]); + + const [isModalInfoActive, setIsModalInfoActive] = useState(false) + const [isModalTrashActive, setIsModalTrashActive] = useState(false) + + const handleModalAction = () => { + setIsModalInfoActive(false) + setIsModalTrashActive(false) + } + + const handleDeleteModalAction = (id: string) => { + setId(id) + setIsModalTrashActive(true) + } + const handleDeleteAction = async () => { + if (id) { + await dispatch(deleteItem(id)); + await loadData(0); + setIsModalTrashActive(false); + } + }; + + const generateFilterRequests = useMemo(() => { + let request = '&'; + filterItems.forEach((item) => { + const isRangeFilter = filters.find( + (filter) => + filter.title === item.fields.selectedField && + (filter.number || filter.date), + ); + + if (isRangeFilter) { + const from = item.fields.filterValueFrom; + const to = item.fields.filterValueTo; + if (from) { + request += `${item.fields.selectedField}Range=${from}&`; + } + if (to) { + request += `${item.fields.selectedField}Range=${to}&`; + } + } else { + const value = item.fields.filterValue; + if (value) { + request += `${item.fields.selectedField}=${value}&`; + } + } + }); + return request; + }, [filterItems, filters]); + + const deleteFilter = (value) => { + const newItems = filterItems.filter((item) => item.id !== value); + + if (newItems.length) { + setFilterItems(newItems); + } else { + loadData(0, ''); + setFilterItems(newItems); + } + }; + + const handleSubmit = () => { + loadData(0, generateFilterRequests); + }; + + const handleChange = (id) => (e) => { + const value = e.target.value; + const name = e.target.name; + + setFilterItems( + filterItems.map((item) => { + if (item.id !== id) return item; + if (name === 'selectedField') return { id, fields: { [name]: value } }; + + return { id, fields: { ...item.fields, [name]: value } } + }), + ); + }; + + const handleReset = () => { + setFilterItems([]); + loadData(0, ''); + }; + + const onPageChange = (page: number) => { + loadData(page); + setCurrentPage(page); + }; + + useEffect(() => { + loadColumns(handleDeleteModalAction, `users`).then((newCols) => + setColumns(newCols), + ); + }, []); + + const handleTableSubmit = async (id: string, data) => { + delete data?.password; + if (!_.isEmpty(data)) { + await dispatch(update({ id, data })) + .unwrap() + .then((res) => res) + .catch((err) => { + throw new Error(err); + }); + } + }; + + const onDeleteRows = async (selectedRows) => { + await dispatch(deleteItemsByIds(selectedRows)); + await loadData(0); + }; + + const controlClasses = + 'w-full py-2 px-2 my-2 border-gray-700 rounded dark:placeholder-gray-400 ' + + ` ${bgColor} ${focusRing} ${corners} ` + + 'dark:bg-slate-800 border'; + + const dataGrid = ( +
    + `datagrid--row`} + rows={users ?? []} + columns={columns} + initialState={{ + pagination: { + paginationModel: { + pageSize: 10, + }, + }, + }} + disableRowSelectionOnClick + onProcessRowUpdateError={(params) => { + console.log('Error', params); + }} + processRowUpdate={async (newRow, oldRow) => { + const data = dataFormatter.dataGridEditFormatter(newRow); + + try { + await handleTableSubmit(newRow.id, data); + return newRow; + } catch { + return oldRow; + } + }} + sortingMode={'server'} + checkboxSelection + onRowSelectionModelChange={(ids) => { + setSelectedRows(ids) + }} + onSortModelChange={(params) => { + params.length + ? setSortModel(params) + : setSortModel([{ field: '', sort: 'desc' }]); + }} + rowCount={count} + pageSizeOptions={[10]} + paginationMode={'server'} + loading={loading} + onPaginationModelChange={(params) => { + onPageChange(params.page); + }} + /> +
    + ) + + return ( + <> + {filterItems && Array.isArray( filterItems ) && filterItems.length ? + + null} + > +
    + <> + {filterItems && filterItems.map((filterItem) => { + return ( +
    +
    +
    Filter
    + + {filters.map((selectOption) => ( + + ))} + +
    + {filters.find((filter) => + filter.title === filterItem?.fields?.selectedField + )?.type === 'enum' ? ( +
    +
    + Value +
    + + + {filters.find((filter) => + filter.title === filterItem?.fields?.selectedField + )?.options?.map((option) => ( + + ))} + +
    + ) : filters.find((filter) => + filter.title === filterItem?.fields?.selectedField + )?.number ? ( +
    +
    +
    From
    + +
    +
    +
    To
    + +
    +
    + ) : filters.find( + (filter) => + filter.title === + filterItem?.fields?.selectedField + )?.date ? ( +
    +
    +
    + From +
    + +
    +
    +
    To
    + +
    +
    + ) : ( +
    +
    Contains
    + +
    + )} +
    +
    Action
    + { + deleteFilter(filterItem.id) + }} + /> +
    +
    + ) + })} +
    + + +
    + +
    +
    +
    : null + } + +

    Are you sure you want to delete this item?

    +
    + {dataGrid} + + {selectedRows.length > 0 && + createPortal( + onDeleteRows(selectedRows)} + />, + document.getElementById('delete-rows-button'), + )} + + + ) +} + +export default TableSampleUsers diff --git a/frontend/src/components/Users/configureUsersCols.tsx b/frontend/src/components/Users/configureUsersCols.tsx new file mode 100644 index 0000000..75554c8 --- /dev/null +++ b/frontend/src/components/Users/configureUsersCols.tsx @@ -0,0 +1,131 @@ +import React from 'react'; +import BaseIcon from '../BaseIcon'; +import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; +import axios from 'axios'; +import { + GridActionsCellItem, + GridRowParams, + GridValueGetterParams, +} from '@mui/x-data-grid'; +import dataFormatter from '../../helpers/dataFormatter' +import DataGridMultiSelect from "../DataGridMultiSelect"; +import ListActionsPopover from '../ListActionsPopover'; +type Params = (id: string) => void; + +export const loadColumns = async ( + onDelete: Params, + entityName: string, +) => { + async function callOptionsApi(entityName: string) { + try { + const data = await axios(`/${entityName}/autocomplete?limit=100`); + return data.data; + } catch (error) { + console.log(error); + return []; + } + } + return [ + + { + field: 'firstName', + headerName: 'First Name', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: true, + + }, + + { + field: 'lastName', + headerName: 'Last Name', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: true, + + }, + + { + field: 'phoneNumber', + headerName: 'Phone Number', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: true, + + }, + + { + field: 'email', + headerName: 'E-Mail', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: true, + + }, + + { + field: 'disabled', + headerName: 'Disabled', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: true, + type: 'boolean', + + }, + + { + field: 'avatar', + headerName: 'Avatar', + flex: 1, + minWidth: 120, + filterable: false, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + + editable: true, + + }, + + { + field: 'actions', + type: 'actions', + minWidth: 30, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + getActions: (params: GridRowParams) => { + + return [ +
    + +
    , + ] + }, + }, + ]; +}; diff --git a/frontend/src/config.ts b/frontend/src/config.ts new file mode 100644 index 0000000..a9783c8 --- /dev/null +++ b/frontend/src/config.ts @@ -0,0 +1,15 @@ +export const hostApi = process.env.NODE_ENV === 'development' && !process.env.NEXT_PUBLIC_BACK_API ? 'http://localhost' : '' +export const portApi = process.env.NODE_ENV === 'development' && !process.env.NEXT_PUBLIC_BACK_API ? 8080 : ''; +export const baseURLApi = `${hostApi}${portApi ? `:${portApi}` : ``}/api` + +export const localStorageDarkModeKey = 'darkMode' + +export const localStorageStyleKey = 'style' + +export const containerMaxW = 'xl:max-w-full xl:mx-auto 2xl:mx-20' + +export const appTitle = 'created by Flatlogic generator!' + +export const getPageTitle = (currentPageTitle: string) => `${currentPageTitle} — ${appTitle}` + +export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || '' diff --git a/frontend/src/css/_app.css b/frontend/src/css/_app.css new file mode 100644 index 0000000..affc655 --- /dev/null +++ b/frontend/src/css/_app.css @@ -0,0 +1,32 @@ +html { + @apply h-full; +} + +body { + @apply pt-14 xl:pl-60 h-full; +} + +#app { + @apply w-screen transition-position lg:w-auto h-full flex flex-col; +} + +.dropdown { + @apply cursor-pointer; +} + +li.stack-item:not(:last-child):after { + content: '/'; + @apply inline-block pl-2; +} + +.m-clipped, .m-clipped body { + @apply overflow-hidden lg:overflow-visible; +} + +.full-screen body { + @apply p-0; +} + +.main-navbar, .app-sidebar-brand { + box-shadow: 0px -1px 40px rgba(112, 144, 176, 0.2); +} diff --git a/frontend/src/css/_calendar.css b/frontend/src/css/_calendar.css new file mode 100644 index 0000000..ce44e3f --- /dev/null +++ b/frontend/src/css/_calendar.css @@ -0,0 +1,37 @@ +.rbc-event { + @apply bg-blue-600 !important; +} + +.rbc-show-more { + @apply dark:text-white bg-transparent !important; +} + +.rbc-btn-group button { + @apply dark:text-white !important; +} + +.rbc-btn-group button:hover { + @apply text-white dark:bg-dark-700 !important; +} + +.rbc-btn-group button.rbc-active { + @apply text-black dark:bg-blue-600 !important; +} + +.rbc-btn-group button:focus { + @apply dark:bg-blue-600 !important; +} + +.rbc-day-bg.rbc-off-range-bg { + @apply dark:bg-dark-800 !important; +} +.rbc-current-time-indicator{ + @apply h-1 !important; +} +.rbc-today { + @apply dark:bg-dark-600/40 !important; +} + +.rbc-day-bg.rbc-selected-cell { + @apply dark:bg-dark-500 !important; +} diff --git a/frontend/src/css/_checkbox-radio-switch.css b/frontend/src/css/_checkbox-radio-switch.css new file mode 100644 index 0000000..a7f567b --- /dev/null +++ b/frontend/src/css/_checkbox-radio-switch.css @@ -0,0 +1,59 @@ +@layer components { + .checkbox, .radio, .switch { + @apply inline-flex items-center cursor-pointer relative; + } + + .checkbox input[type=checkbox], .radio input[type=radio], .switch input[type=checkbox] { + @apply absolute left-0 opacity-0 -z-1; + } + + .checkbox input[type=checkbox]+.check, .radio input[type=radio]+.check, .switch input[type=checkbox]+.check { + @apply border-gray-700 border transition-colors duration-200 dark:bg-slate-800; + } + + .checkbox input[type=checkbox]:focus+.check, .radio input[type=radio]:focus+.check, .switch input[type=checkbox]:focus+.check { + @apply ring ring-blue-700; + } + + .checkbox input[type=checkbox]+.check, .radio input[type=radio]+.check { + @apply block w-5 h-5; + } + + .checkbox input[type=checkbox]+.check { + @apply rounded; + } + + .switch input[type=checkbox]+.check { + @apply flex items-center shrink-0 w-12 h-6 p-0.5 bg-gray-200; + } + + .radio input[type=radio]+.check, .switch input[type=checkbox]+.check, .switch input[type=checkbox]+.check:before { + @apply rounded-full; + } + + .checkbox input[type=checkbox]:checked+.check, .radio input[type=radio]:checked+.check { + @apply bg-no-repeat bg-center border-4; + } + + .checkbox input[type=checkbox]:checked+.check { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath style='fill:%23fff' d='M 0.04038059,0.6267767 0.14644661,0.52071068 0.42928932,0.80355339 0.3232233,0.90961941 z M 0.21715729,0.80355339 0.85355339,0.16715729 0.95961941,0.2732233 0.3232233,0.90961941 z'%3E%3C/path%3E%3C/svg%3E"); + } + + .radio input[type=radio]:checked+.check { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z' /%3E%3C/svg%3E"); + } + + .switch input[type=checkbox]:checked+.check, .checkbox input[type=checkbox]:checked+.check, .radio input[type=radio]:checked+.check { + @apply bg-blue-600 border-blue-600; + } + + .switch input[type=checkbox]+.check:before { + content: ''; + @apply block w-5 h-5 bg-white border border-gray-700; + } + + .switch input[type=checkbox]:checked+.check:before { + transform: translate3d(110%, 0 ,0); + @apply border-blue-600; + } +} \ No newline at end of file diff --git a/frontend/src/css/_helper.css b/frontend/src/css/_helper.css new file mode 100644 index 0000000..3444fa9 --- /dev/null +++ b/frontend/src/css/_helper.css @@ -0,0 +1,23 @@ +.helper-container { + right: 0; + top: 70px; + transform: translateX(100%); + + .tab { + top: 0; + left: 0; + transform: translateX(-100%); + } + + .tab:hover { + @apply bg-gray-900 cursor-pointer; + } +} + +.helper-container.open { + transform: translateX(0); +} + +.react-datepicker-wrapper, .react-datepicker-popper { + z-index: 10 !important; +} diff --git a/frontend/src/css/_progress.css b/frontend/src/css/_progress.css new file mode 100644 index 0000000..65d6796 --- /dev/null +++ b/frontend/src/css/_progress.css @@ -0,0 +1,21 @@ +@layer base { + progress { + @apply h-3 rounded-full overflow-hidden; + } + + progress::-webkit-progress-bar { + @apply bg-blue-200; + } + + progress::-webkit-progress-value { + @apply bg-blue-500; + } + + progress::-moz-progress-bar { + @apply bg-blue-500; + } + + progress::-ms-fill { + @apply bg-blue-500 border-0; + } +} diff --git a/frontend/src/css/_scrollbars.css b/frontend/src/css/_scrollbars.css new file mode 100644 index 0000000..a181b84 --- /dev/null +++ b/frontend/src/css/_scrollbars.css @@ -0,0 +1,41 @@ +@layer base { + html { + scrollbar-width: thin; + scrollbar-color: rgb(156, 163, 175) rgb(249, 250, 251); + } + + body::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + body::-webkit-scrollbar-track { + @apply bg-gray-50; + } + + body::-webkit-scrollbar-thumb { + @apply bg-gray-400 rounded; + } + + body::-webkit-scrollbar-thumb:hover { + @apply bg-gray-500; + } +} + +@layer utilities { + .dark-scrollbars-compat { + scrollbar-color: rgb(71, 85, 105) rgb(30, 41, 59); + } + + .dark-scrollbars::-webkit-scrollbar-track { + @apply bg-slate-800; + } + + .dark-scrollbars::-webkit-scrollbar-thumb { + @apply bg-slate-600; + } + + .dark-scrollbars::-webkit-scrollbar-thumb:hover { + @apply bg-slate-500; + } +} diff --git a/frontend/src/css/_select-dropdown.css b/frontend/src/css/_select-dropdown.css new file mode 100644 index 0000000..040ae0d --- /dev/null +++ b/frontend/src/css/_select-dropdown.css @@ -0,0 +1,31 @@ +.react-select__control { + @apply dark:bg-dark-800 dark:border-dark-700 !important; +} + +.react-select__single-value { + @apply dark:text-white !important; +} + +.react-select__menu { + @apply dark:border-dark-700 +} + +.react-select__menu-list { + @apply dark:bg-dark-800 dark:border-dark-700 dark:rounded !important; +} + +.react-select__option { + @apply cursor-pointer hover:bg-gray-200 dark:hover:bg-dark-700 !important; +} + +.react-select__option--is-focused { + @apply dark:bg-dark-800 dark:text-white hover:dark:bg-dark-700 hover:dark:text-white !important; +} + +.react-select__option--is-selected, .react-select__option--is-selected:hover { + @apply dark:bg-dark-600 !important; +} + +.react-select__multi-value__remove { + @apply dark:bg-dark-600 dark:text-white hover:dark:bg-red-300 hover:dark:text-red-600 !important; +} diff --git a/frontend/src/css/_table.css b/frontend/src/css/_table.css new file mode 100644 index 0000000..fc149c9 --- /dev/null +++ b/frontend/src/css/_table.css @@ -0,0 +1,115 @@ +@layer base { + table { + @apply w-full; + } + + thead { + @apply hidden lg:table-header-group; + } + + tr { + @apply max-w-full block relative border-b-4 border-gray-100 + lg:table-row lg:border-b-0 dark:border-slate-800; + } + + tr:last-child { + @apply border-b-0; + } + + td:not(:first-child) { + @apply lg:border-l lg:border-t-0 lg:border-r-0 lg:border-b-0 lg:border-gray-100 lg:dark:border-slate-700; + } + + th { + @apply lg:text-left lg:p-3 border-b; + } + + th.sortable { + cursor: pointer; + } + + th.sortable:hover:after { + transition: all 1s; + position: absolute; + + content: "↕"; + + margin-left: 1rem; + } + + th.sortable.asc:hover:after { + content: "↑"; + } + th.sortable.desc:hover:after { + content: "↓"; + } + + td { + @apply flex justify-between text-right py-3 px-4 align-top border-b border-gray-100 + lg:table-cell lg:text-left lg:p-3 lg:align-middle lg:border-b-0 dark:border-slate-800 dark:text-white; + } + + td:last-child { + @apply border-b-0; + } + + tbody tr, tbody tr:nth-child(odd) { + @apply lg:hover:bg-brand-300/70; + } + + tbody tr:nth-child(even) { + @apply lg:bg-brand-300 dark:bg-brand-300/70; + } + + td:before { + content: attr(data-label); + @apply font-semibold pr-3 text-left lg:hidden; + } + + tbody tr td { + @apply text-sm font-normal text-brand-900 dark:text-white; + } + + .datagrid--table, .MuiDataGrid-root { + @apply rounded border-none !important; + } + + .datagrid--header { + @apply uppercase !important; + } + + .datagrid--header, + .datagrid--header .MuiIconButton-root, + .datagrid--cell, + .datagrid--cell .MuiIconButton-root { + @apply dark:text-white; + } + + .datagrid--cell .MuiDataGrid-booleanCell { + @apply dark:text-white !important; + } + + .datagrid--cell .MuiIconButton-root:hover { + @apply dark:text-white dark:bg-dark-700; + } + + .datagrid--row { + @apply even:bg-gray-100 dark:even:bg-[#1B1D22] dark:odd:bg-dark-900 !important; + } + + .datagrid--table .MuiTablePagination-root { + @apply dark:text-white; + } + + .datagrid--table .MuiTablePagination-root .MuiButtonBase-root:disabled { + @apply dark:text-dark-700; + } + + .datagrid--table .MuiTablePagination-root .MuiButtonBase-root:hover { + @apply dark:bg-dark-700; + } + + .MuiButton-colorInherit { + @apply text-blue-600 dark:text-dark-700 !important; + } +} diff --git a/frontend/src/css/_theme.css b/frontend/src/css/_theme.css new file mode 100644 index 0000000..489bdf5 --- /dev/null +++ b/frontend/src/css/_theme.css @@ -0,0 +1,103 @@ + +.theme-pink { +.app-sidebar { + @apply bg-brand-900 text-white; + +.menu-title, +.menu-item-icon, +.menu-item-link { + @apply text-white; +} +} + +.app-sidebar-brand { + @apply bg-white; +} + +.bg-blue-600 { + @apply bg-brand-800; +} + +.border-blue-700 { + @apply border-pink-700; +} + +.checkbox input[type=checkbox]:checked + .check, .radio input[type=radio]:checked + .check { + @apply border-brand-800; +} + +.helper-container .tab { + @apply bg-brand-900; +} + +.focus\:ring:focus { + --tw-ring-color: #14142A; +} + +.checkbox input[type=checkbox]:focus + .check, .radio input[type=radio]:focus + .check, .switch input[type=checkbox]:focus + .check { + --tw-ring-color: #14142A; +} + +} + +.theme-green { + +.app-sidebar { + @apply bg-brand-800 text-white; + +.menu-title, +.menu-item-icon, +.menu-item-link { + @apply text-white; +} + +} + +.app-sidebar-brand { + @apply bg-white; +} + +.bg-blue-600 { + @apply bg-brand-800; +} + +.border-blue-700 { + @apply bg-brand-700; +} + +.hover\:bg-blue-700:hover { + @apply bg-brand-700; +} + +.text-blue-600 { + @apply text-brand-900; +} + +.checkbox input[type=checkbox]:checked + .check, .radio input[type=radio]:checked + .check { + @apply border-brand-800; +} + +.helper-container .tab { + @apply bg-brand-700; +} + +.focus\:ring:focus { + --tw-ring-color: #4E4B66; +} + +.checkbox input[type=checkbox]:focus + .check, .radio input[type=radio]:focus + .check, .switch input[type=checkbox]:focus + .check { + --tw-ring-color: #4E4B66; +} + +.text-blue-500 { + @apply text-brand-800; +} + +.hover\:text-blue-600:hover { + @apply text-brand-800; +} + +.active\:text-blue-700:active { + @apply text-brand-800; +} +} diff --git a/frontend/src/css/main.css b/frontend/src/css/main.css new file mode 100644 index 0000000..2886dd7 --- /dev/null +++ b/frontend/src/css/main.css @@ -0,0 +1,33 @@ +@import "tailwind/_base.css"; +@import "tailwind/_components.css"; +@import "tailwind/_utilities.css"; +@import 'intro.js/introjs.css'; +@import "_checkbox-radio-switch.css"; +@import "_progress.css"; +@import "_scrollbars.css"; +@import "_table.css"; +@import "_helper.css"; +@import '_calendar.css'; +@import '_select-dropdown.css'; +@import "_theme.css"; + +.introjs-tooltip { + @apply min-w-[400px] max-w-[480px] p-2 !important; +} + +.good-img { + @apply -mt-96 !important; +} +.end-img { + @apply -mt-72 !important; +} +.introjs-button { + @apply bg-blue-600 text-white !important; + text-shadow: none !important; +} +.introjs-bullets ul li a.active { + @apply bg-blue-600 !important; +} +.introjs-prevbutton{ + @apply bg-transparent border border-blue-600 text-blue-600 !important; +} diff --git a/frontend/src/css/tailwind/_base.css b/frontend/src/css/tailwind/_base.css new file mode 100644 index 0000000..2f02db5 --- /dev/null +++ b/frontend/src/css/tailwind/_base.css @@ -0,0 +1 @@ +@tailwind base; diff --git a/frontend/src/css/tailwind/_components.css b/frontend/src/css/tailwind/_components.css new file mode 100644 index 0000000..020aaba --- /dev/null +++ b/frontend/src/css/tailwind/_components.css @@ -0,0 +1 @@ +@tailwind components; diff --git a/frontend/src/css/tailwind/_utilities.css b/frontend/src/css/tailwind/_utilities.css new file mode 100644 index 0000000..65dd5f6 --- /dev/null +++ b/frontend/src/css/tailwind/_utilities.css @@ -0,0 +1 @@ +@tailwind utilities; diff --git a/frontend/src/helpers/dataFormatter.js b/frontend/src/helpers/dataFormatter.js new file mode 100644 index 0000000..93bc1ee --- /dev/null +++ b/frontend/src/helpers/dataFormatter.js @@ -0,0 +1,42 @@ +import dayjs from 'dayjs'; +import _ from 'lodash'; + +export default { + filesFormatter(arr) { + if (!arr || !arr.length) return []; + return arr.map((item) => item); + }, + imageFormatter(arr) { + if (!arr || !arr.length) return [] + return arr.map(item => ({ + publicUrl: item.publicUrl || '' + })) + }, + oneImageFormatter(arr) { + if (!arr || !arr.length) return '' + return arr[0].publicUrl || '' + }, + dateFormatter(date) { + if (!date) return '' + return dayjs(date).format('YYYY-MM-DD') + }, + dateTimeFormatter(date) { + if (!date) return '' + return dayjs(date).format('YYYY-MM-DD HH:mm') + }, + booleanFormatter(val) { + return val ? 'Yes' : 'No' + }, + dataGridEditFormatter(obj) { + return _.transform(obj, (result, value, key) => { + if (_.isArray(value)) { + result[key] = _.map(value, 'id'); + } else if (_.isObject(value)) { + result[key] = value.id; + } else { + result[key] = value; + } + }); + }, + +} diff --git a/frontend/src/helpers/fileSaver.ts b/frontend/src/helpers/fileSaver.ts new file mode 100644 index 0000000..eff647d --- /dev/null +++ b/frontend/src/helpers/fileSaver.ts @@ -0,0 +1,6 @@ +import { saveAs } from "file-saver"; + +export const saveFile = (e, url: string, name: string) => { + e.stopPropagation(); + saveAs(url,name); +}; diff --git a/frontend/src/helpers/humanize.ts b/frontend/src/helpers/humanize.ts new file mode 100644 index 0000000..ea88c06 --- /dev/null +++ b/frontend/src/helpers/humanize.ts @@ -0,0 +1,11 @@ +export function humanize(str: string) { + if (!str) { + return ''; + } + return str.toString() + .replace(/^[\s_]+|[\s_]+$/g, '') + .replace(/[_\s]+/g, ' ') + .replace(/^[a-z]/, function (m) { + return m.toUpperCase(); + }); + } \ No newline at end of file diff --git a/frontend/src/helpers/notifyStateHandler.ts b/frontend/src/helpers/notifyStateHandler.ts new file mode 100644 index 0000000..b1a247c --- /dev/null +++ b/frontend/src/helpers/notifyStateHandler.ts @@ -0,0 +1,31 @@ +export const resetNotify = (state) => { + state.notify.showNotification = false; + state.notify.typeNotification = ''; + state.notify.textNotification = ''; +}; +export const rejectNotify = (state, action) => { + if (typeof action.payload === 'string') { + state.notify.textNotification = action.payload; + } else if (typeof action === 'object') { + const obj = { ...action.payload?.errors }; + delete obj['_errors']; + + let msg = ''; + + for (const key in obj) { + msg += `${key}: ${obj[key]['_errors']}; \n `; + } + + state.notify.textNotification = msg; + } else { + state.notify.textNotification = 'Network error'; + } + state.notify.textNotification = state.notify.textNotification || 'Network error'; + state.notify.typeNotification = 'error'; + state.notify.showNotification = true; +}; +export const fulfilledNotify = (state, msg) => { + state.notify.textNotification = msg; + state.notify.typeNotification = 'success'; + state.notify.showNotification = true; +}; diff --git a/frontend/src/hooks/sampleData.ts b/frontend/src/hooks/sampleData.ts new file mode 100644 index 0000000..7e2aeec --- /dev/null +++ b/frontend/src/hooks/sampleData.ts @@ -0,0 +1,22 @@ +import useSWR from 'swr' +const fetcher = (url: string) => fetch(url).then((res) => res.json()) + +export const useSampleClients = () => { + const { data, error } = useSWR('/data-sources/clients.json', fetcher) + + return { + clients: data?.data ?? [], + isLoading: !error && !data, + isError: error, + } +} + +export const useSampleTransactions = () => { + const { data, error } = useSWR('/data-sources/history.json', fetcher) + + return { + transactions: data?.data ?? [], + isLoading: !error && !data, + isError: error, + } +} diff --git a/frontend/src/hooks/useDevCompilationStatus.ts b/frontend/src/hooks/useDevCompilationStatus.ts new file mode 100644 index 0000000..dd07dba --- /dev/null +++ b/frontend/src/hooks/useDevCompilationStatus.ts @@ -0,0 +1,44 @@ +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/router'; + +type CompilationStatus = 'ready' | 'compiling' | 'error' | 'initial'; + +const useDevCompilationStatus = (): CompilationStatus => { + const router = useRouter(); + const [status, setStatus] = useState('initial'); + + useEffect(() => { + if (process.env.NODE_ENV !== 'development') { + setStatus('ready'); + return; + } + + const handleRouteChangeStart = () => { + setStatus('compiling'); + }; + + const handleRouteChangeComplete = () => { + setTimeout(() => setStatus('ready'), 300); + }; + + const handleRouteChangeError = () => { + setTimeout(() => setStatus('error'), 300); + }; + + router.events.on('routeChangeStart', handleRouteChangeStart); + router.events.on('routeChangeComplete', handleRouteChangeComplete); + router.events.on('routeChangeError', handleRouteChangeError); + + setStatus('ready'); + + return () => { + router.events.off('routeChangeStart', handleRouteChangeStart); + router.events.off('routeChangeComplete', handleRouteChangeComplete); + router.events.off('routeChangeError', handleRouteChangeError); + }; + }, [router]); + + return status; +}; + +export default useDevCompilationStatus; \ No newline at end of file diff --git a/frontend/src/interfaces/index.ts b/frontend/src/interfaces/index.ts new file mode 100644 index 0000000..0c7dd74 --- /dev/null +++ b/frontend/src/interfaces/index.ts @@ -0,0 +1,109 @@ +export type UserPayloadObject = { + name: string + email: string + avatar: string +} + +export type MenuAsideItem = { + label: string + icon?: string + href?: string + target?: string + color?: ColorButtonKey + isLogout?: boolean + withDevider?: boolean; + menu?: MenuAsideItem[] + permissions?: string | string[] +} + +export type MenuNavBarItem = { + label?: string + icon?: string + href?: string + target?: string + isDivider?: boolean + isLogout?: boolean + isDesktopNoLabel?: boolean + isToggleLightDark?: boolean + isCurrentUser?: boolean + menu?: MenuNavBarItem[] +} + +export type ColorKey = 'white' | 'light' | 'contrast' | 'success' | 'danger' | 'warning' | 'info' + +export type ColorButtonKey = + | 'white' + | 'whiteDark' + | 'lightDark' + | 'contrast' + | 'success' + | 'danger' + | 'warning' + | 'info' + | 'void' + +export type BgKey = 'purplePink' | 'pinkRed' | 'violet' + +export type TrendType = 'up' | 'down' | 'success' | 'danger' | 'warning' | 'info' + +export type TransactionType = 'withdraw' | 'deposit' | 'invoice' | 'payment' + +export type Transaction = { + id: number + amount: number + account: string + name: string + date: string + type: TransactionType + business: string +} + +export type Client = { + id: number + avatar: string + login: string + name: string + city: string, + company: string + firstName: string + lastName: string + phoneNumber: string + email: string + progress: number, + role: string + disabled: boolean + created: string + created_mm_dd_yyyy: string +} + +export interface User { + id: string; + firstName: string; + lastName?: any; + phoneNumber?: any; + email: string; + role: string; + disabled: boolean; + password: string; + emailVerified: boolean; + emailVerificationToken?: any; + emailVerificationTokenExpiresAt?: any; + passwordResetToken?: any; + passwordResetTokenExpiresAt?: any; + provider: string; + importHash?: any; + createdAt: Date; + updatedAt: Date; + deletedAt?: any; + createdById?: any; + updatedById?: any; + avatar: any[]; + notes: any[]; +} + +export type StyleKey = 'white' | 'basic' + +export type UserForm = { + name: string + email: string +} diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx new file mode 100644 index 0000000..36ccc7e --- /dev/null +++ b/frontend/src/layouts/Authenticated.tsx @@ -0,0 +1,111 @@ +import React, { ReactNode, useEffect } from 'react' +import { useState } from 'react' +import jwt from 'jsonwebtoken'; +import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' +import menuAside from '../menuAside' +import menuNavBar from '../menuNavBar' +import BaseIcon from '../components/BaseIcon' +import NavBar from '../components/NavBar' +import NavBarItemPlain from '../components/NavBarItemPlain' +import AsideMenu from '../components/AsideMenu' +import FooterBar from '../components/FooterBar' +import { useAppDispatch, useAppSelector } from '../stores/hooks' +import Search from '../components/Search'; +import { useRouter } from 'next/router' +import {findMe, logoutUser} from "../stores/authSlice"; + +type Props = { + children: ReactNode +} + +export default function LayoutAuthenticated({ + children, +}: Props) { + const dispatch = useAppDispatch() + const router = useRouter() + const { token, currentUser } = useAppSelector((state) => state.auth) + const bgColor = useAppSelector((state) => state.style.bgLayoutColor); + let localToken + if (typeof window !== 'undefined') { + // Perform localStorage action + localToken = localStorage.getItem('token') + } + + const isTokenValid = () => { + const token = localStorage.getItem('token'); + if (!token) return; + const date = new Date().getTime() / 1000; + const data = jwt.decode(token); + if (!data) return; + return date < data.exp; + }; + + useEffect(() => { + dispatch(findMe()); + if (!isTokenValid()) { + dispatch(logoutUser()); + router.push('/login'); + } + }, [token, localToken]); + + const darkMode = useAppSelector((state) => state.style.darkMode) + + const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false) + const [isAsideLgActive, setIsAsideLgActive] = useState(false) + + useEffect(() => { + const handleRouteChangeStart = () => { + setIsAsideMobileExpanded(false) + setIsAsideLgActive(false) + } + + router.events.on('routeChangeStart', handleRouteChangeStart) + + // If the component is unmounted, unsubscribe + // from the event with the `off` method: + return () => { + router.events.off('routeChangeStart', handleRouteChangeStart) + } + }, [router.events, dispatch]) + + const layoutAsidePadding = 'xl:pl-60' + + return ( +
    +
    + + setIsAsideMobileExpanded(!isAsideMobileExpanded)} + > + + + setIsAsideLgActive(true)} + > + + + + + + + setIsAsideLgActive(false)} + /> + {children} + Hand-crafted & Made with ❤️ +
    +
    + ) +} diff --git a/frontend/src/layouts/Guest.tsx b/frontend/src/layouts/Guest.tsx new file mode 100644 index 0000000..657813c --- /dev/null +++ b/frontend/src/layouts/Guest.tsx @@ -0,0 +1,17 @@ +import React, { ReactNode } from 'react' +import { useAppSelector } from '../stores/hooks' + +type Props = { + children: ReactNode +} + +export default function LayoutGuest({ children }: Props) { + const darkMode = useAppSelector((state) => state.style.darkMode) + const bgColor = useAppSelector((state) => state.style.bgLayoutColor); + + return ( +
    +
    {children}
    +
    + ) +} diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts new file mode 100644 index 0000000..7a2306b --- /dev/null +++ b/frontend/src/menuAside.ts @@ -0,0 +1,34 @@ +import * as icon from '@mdi/js'; +import { MenuAsideItem } from './interfaces' + +const menuAside: MenuAsideItem[] = [ + { + href: '/dashboard', + icon: icon.mdiViewDashboardOutline, + label: 'Dashboard', + }, + + { + href: '/users/users-list', + label: 'Users', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + icon: icon.mdiAccountGroup ?? icon.mdiTable, + permissions: 'READ_USERS' + }, + { + href: '/profile', + label: 'Profile', + icon: icon.mdiAccountCircle, + }, + + { + href: '/api-docs', + target: '_blank', + label: 'Swagger API', + icon: icon.mdiFileCode, + permissions: 'READ_API_DOCS' + }, +] + +export default menuAside diff --git a/frontend/src/menuNavBar.ts b/frontend/src/menuNavBar.ts new file mode 100644 index 0000000..8f972ea --- /dev/null +++ b/frontend/src/menuNavBar.ts @@ -0,0 +1,49 @@ +import { + mdiMenu, + mdiClockOutline, + mdiCloud, + mdiCrop, + mdiAccount, + mdiCogOutline, + mdiEmail, + mdiLogout, + mdiThemeLightDark, + mdiGithub, + mdiVuejs, +} from '@mdi/js' +import { MenuNavBarItem } from './interfaces' + +const menuNavBar: MenuNavBarItem[] = [ + { + isCurrentUser: true, + menu: [ + { + icon: mdiAccount, + label: 'My Profile', + href: '/profile', + }, + { + isDivider: true, + }, + { + icon: mdiLogout, + label: 'Log Out', + isLogout: true, + }, + ], + }, + { + icon: mdiThemeLightDark, + label: 'Light/Dark', + isDesktopNoLabel: true, + isToggleLightDark: true, + }, + { + icon: mdiLogout, + label: 'Log out', + isDesktopNoLabel: true, + isLogout: true, + }, +] + +export default menuNavBar diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx new file mode 100644 index 0000000..6a831da --- /dev/null +++ b/frontend/src/pages/_app.tsx @@ -0,0 +1,122 @@ +import React from 'react'; +import type { AppProps } from 'next/app'; +import type { ReactElement, ReactNode } from 'react'; +import type { NextPage } from 'next'; +import Head from 'next/head'; +import { store } from '../stores/store'; +import { Provider } from 'react-redux'; +import '../css/main.css'; +import axios from 'axios'; +import { baseURLApi } from '../config'; +import { useRouter } from 'next/router'; +import ErrorBoundary from "../components/ErrorBoundary"; +import DevModeBadge from '../components/DevModeBadge'; + +// Initialize axios +axios.defaults.baseURL = process.env.NEXT_PUBLIC_BACK_API + ? process.env.NEXT_PUBLIC_BACK_API + : baseURLApi; + +axios.defaults.headers.common['Content-Type'] = 'application/json'; + +export type NextPageWithLayout

    , IP = P> = NextPage & { + getLayout?: (page: ReactElement) => ReactNode +} + +type AppPropsWithLayout = AppProps & { + Component: NextPageWithLayout +} + +function MyApp({ Component, pageProps }: AppPropsWithLayout) { + // Use the layout defined at the page level, if available + const getLayout = Component.getLayout || ((page) => page); + const router = useRouter(); + + axios.interceptors.request.use( + config => { + const token = localStorage.getItem('token'); + + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } else { + delete config.headers.Authorization; + } + + return config; + }, + error => { + return Promise.reject(error); + } + ); + + React.useEffect(() => { + const handleMessage = async (event: MessageEvent) => { + if (event.data === 'getLocation') { + event.source?.postMessage( + { iframeLocation: window.location.pathname }, + event.origin, + ); + return; + } + + if (event.data === 'getScreenshot') { + try { + const html2canvas = (await import('html2canvas')).default; + const canvas = await html2canvas(document.body, { useCORS: true }); + const url = canvas.toDataURL('image/jpeg', 0.8); + event.source?.postMessage({ iframeScreenshot: url }, event.origin); + } catch (e) { + console.error('html2canvas failed', e); + event.source?.postMessage({ iframeScreenshot: null }, event.origin); + } + } + }; + + window.addEventListener('message', handleMessage); + return () => window.removeEventListener('message', handleMessage); + }, []); + + const title = 'betterp' + const description = 'betterp generated by Flatlogic' + const url = "https://flatlogic.com/" + const image = `https://flatlogic.com/logo.svg` + const imageWidth = '1920' + const imageHeight = '960' + + return ( + + {getLayout( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + {(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') && } + + )} + + ) +} + +export default MyApp; diff --git a/frontend/src/pages/api/hello.js b/frontend/src/pages/api/hello.js new file mode 100644 index 0000000..f163396 --- /dev/null +++ b/frontend/src/pages/api/hello.js @@ -0,0 +1,5 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction + +export default function helloAPI(req, res) { + res.status(200).json({ name: 'John Doe' }) +} diff --git a/frontend/src/pages/api/logError.ts b/frontend/src/pages/api/logError.ts new file mode 100644 index 0000000..d72b3fd --- /dev/null +++ b/frontend/src/pages/api/logError.ts @@ -0,0 +1,83 @@ +import fsPromises from 'fs/promises'; +import path from 'path'; + +const dataFilePath = path.join(process.cwd(), 'json/runtimeError.json'); + +export default async function handler(req, res) { + // Ensure directory exists + try { + await fsPromises.mkdir(path.dirname(dataFilePath), { recursive: true }); + } catch (error) { + // Ignore if directory already exists + } + + if (req.method === 'GET') { + try { + // Check if file exists + try { + await fsPromises.access(dataFilePath); + } catch (error) { + // File doesn't exist, return empty object + return res.status(200).json({}); + } + + // Read the existing data from the JSON file + const jsonData = await fsPromises.readFile(dataFilePath, 'utf-8'); + + // Handle empty file + if (!jsonData || jsonData.trim() === '') { + // Write empty JSON object to file + await fsPromises.writeFile(dataFilePath, '{}', 'utf-8'); + return res.status(200).json({}); + } + + // Parse JSON data + try { + const objectData = JSON.parse(jsonData); + return res.status(200).json(objectData); + } catch (parseError) { + console.error('Error parsing JSON from file:', parseError); + // Reset the file with valid JSON if parsing fails + await fsPromises.writeFile(dataFilePath, '{}', 'utf-8'); + return res.status(200).json({}); + } + } catch (error) { + console.error('Error in GET handler:', error); + return res.status(200).json({}); // Return empty object instead of error + } + } else if (req.method === 'POST') { + try { + const updatedData = JSON.stringify(req.body); + + // Create directory if it doesn't exist + await fsPromises.mkdir(path.dirname(dataFilePath), { recursive: true }); + + // Write the updated data to the JSON file + await fsPromises.writeFile(dataFilePath, updatedData); + + // Send a success response + res.status(200).json({ message: 'Data stored successfully' }); + } catch (error) { + console.error('Error in POST handler:', error); + // Send an error response + res.status(500).json({ message: 'Error storing data' }); + } + } else if (req.method === 'DELETE') { + try { + // Create directory if it doesn't exist + await fsPromises.mkdir(path.dirname(dataFilePath), { recursive: true }); + + // Write empty JSON object to file + await fsPromises.writeFile(dataFilePath, '{}'); + + // Send a success response + res.status(200).json({message: 'Data deleted successfully'}); + } catch (error) { + console.error('Error in DELETE handler:', error); + // Send an error response + res.status(500).json({message: 'Error deleting data'}); + } + } else { + res.status(405).json({ message: 'Method not allowed' }); + } +} diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx new file mode 100644 index 0000000..c3729f0 --- /dev/null +++ b/frontend/src/pages/dashboard.tsx @@ -0,0 +1,100 @@ +import * as icon from '@mdi/js'; +import Head from 'next/head' +import React from 'react' +import axios from 'axios'; +import type { ReactElement } from 'react' +import LayoutAuthenticated from '../layouts/Authenticated' +import SectionMain from '../components/SectionMain' +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' +import BaseIcon from "../components/BaseIcon"; + +import Link from "next/link"; +import { useAppSelector } from '../stores/hooks'; +const Dashboard = () => { + const iconsColor = useAppSelector((state) => state.style.iconsColor); + const corners = useAppSelector((state) => state.style.corners); + const cardsStyle = useAppSelector((state) => state.style.cardsStyle); + + const loadingMessage = 'Loading...' + + const [users, setUsers] = React.useState(loadingMessage); + + async function loadData() { + const entities = ['users',]; + const fns = [setUsers,]; + + const requests = entities.map((entity, index) => { + return axios.get(`/${entity.toLowerCase()}/count`); + }); + + Promise.allSettled(requests).then((results) => { + results.forEach((result, i) => { + if (result.status === 'fulfilled') { + fns[i](result.value.data.count); + } else { + fns[i](result.reason.message); + } + }); + }); + } + + React.useEffect(() => { + loadData().then(); + }, []); + + return ( + <> + + + {'Overview'} + + + + + {''} + + +

    + + +
    +
    +
    +
    + Users +
    +
    + {users} +
    +
    +
    + +
    +
    +
    + + +
    + + + ) +} + +Dashboard.getLayout = function getLayout(page: ReactElement) { + return {page} +} + +export default Dashboard diff --git a/frontend/src/pages/error.tsx b/frontend/src/pages/error.tsx new file mode 100644 index 0000000..4a6116e --- /dev/null +++ b/frontend/src/pages/error.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import type { ReactElement } from 'react' +import Head from 'next/head' +import BaseButton from '../components/BaseButton' +import CardBox from '../components/CardBox' +import SectionFullScreen from '../components/SectionFullScreen' +import LayoutGuest from '../layouts/Guest' +import { getPageTitle } from '../config' + +export default function Error() { + return ( + <> + + {getPageTitle('Error')} + + + + } + > +
    +

    Unhandled exception

    + +

    An Error Occurred

    +
    +
    +
    + + ) +} + +Error.getLayout = function getLayout(page: ReactElement) { + return {page} +} diff --git a/frontend/src/pages/forgot.tsx b/frontend/src/pages/forgot.tsx new file mode 100644 index 0000000..39b0193 --- /dev/null +++ b/frontend/src/pages/forgot.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import type { ReactElement } from 'react'; +import { ToastContainer, toast } from 'react-toastify'; +import Head from 'next/head'; +import BaseButton from '../components/BaseButton'; +import CardBox from '../components/CardBox'; +import SectionFullScreen from '../components/SectionFullScreen'; +import LayoutGuest from '../layouts/Guest'; +import { Field, Form, Formik } from 'formik'; +import FormField from '../components/FormField'; +import BaseDivider from '../components/BaseDivider'; +import BaseButtons from '../components/BaseButtons'; +import { useRouter } from 'next/router'; +import { getPageTitle } from '../config'; +import axios from "axios"; + +export default function Forgot() { + const [loading, setLoading] = React.useState(false) + const router = useRouter(); + const notify = (type, msg) => toast( msg, {type}); + + const handleSubmit = async (value) => { + setLoading(true) + try { + const { data: response } = await axios.post('/auth/send-password-reset-email', value); + setLoading(false) + notify('success', 'Please check your email for verification link'); + setTimeout(async () => { + await router.push('/login') + }, 3000) + } catch (error) { + setLoading(false) + console.log('error: ', error) + notify('error', 'Something was wrong. Try again') + } + }; + + return ( + <> + + {getPageTitle('Login')} + + + + + handleSubmit(values)} + > +
    + + + + + + + + + + + +
    +
    +
    + + + ); +} + +Forgot.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/forms.tsx b/frontend/src/pages/forms.tsx new file mode 100644 index 0000000..093750e --- /dev/null +++ b/frontend/src/pages/forms.tsx @@ -0,0 +1,142 @@ +import { mdiAccount, mdiBallotOutline, mdiGithub, mdiMail, mdiUpload } from '@mdi/js' +import { Field, Form, Formik } from 'formik' +import Head from 'next/head' +import { ReactElement } from 'react' +import BaseButton from '../components/BaseButton' +import BaseButtons from '../components/BaseButtons' +import BaseDivider from '../components/BaseDivider' +import CardBox from '../components/CardBox' +import FormCheckRadio from '../components/FormCheckRadio' +import FormCheckRadioGroup from '../components/FormCheckRadioGroup' +import FormField from '../components/FormField' +import LayoutAuthenticated from '../layouts/Authenticated' +import SectionMain from '../components/SectionMain' +import SectionTitle from '../components/SectionTitle' +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' +import { getPageTitle } from '../config' + +const FormsPage = () => { + return ( + <> + + {getPageTitle('Forms')} + + + + + {''} + + + + alert(JSON.stringify(values, null, 2))} + > +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + Custom elements + + + + null} + > +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    + + ) +} + +FormsPage.getLayout = function getLayout(page: ReactElement) { + return {page} +} + +export default FormsPage diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx new file mode 100644 index 0000000..dd3ea8a --- /dev/null +++ b/frontend/src/pages/index.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import type { ReactElement } from 'react'; +import Head from 'next/head'; +import Link from 'next/link'; +import BaseButton from '../components/BaseButton'; +import CardBox from '../components/CardBox'; +import SectionFullScreen from '../components/SectionFullScreen'; +import LayoutGuest from '../layouts/Guest'; +import BaseButtons from '../components/BaseButtons'; +import { getPageTitle } from '../config'; +import { useAppSelector } from '../stores/hooks'; +import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; + +export default function Starter() { + const textColor = useAppSelector((state) => state.style.linkColor); + const title = 'betterp' + return ( +
    + + {getPageTitle('Starter Page')} + + + +
    +
    + + + +
    +

    This is a React.js/Node.js app generated by the Flatlogic Web App Generator

    +

    For guides and documentation please check + your local README.md and the Flatlogic documentation

    +
    + + + + + + +
    +
    +
    +
    +
    +

    © 2024 {title}. All rights reserved

    + + Privacy Policy + +
    +
    + ); +} + +Starter.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx new file mode 100644 index 0000000..23298ae --- /dev/null +++ b/frontend/src/pages/login.tsx @@ -0,0 +1,198 @@ +import React, { useEffect, useState } from 'react'; +import type { ReactElement } from 'react'; +import Head from 'next/head'; +import BaseButton from '../components/BaseButton'; +import CardBox from '../components/CardBox'; +import BaseIcon from "../components/BaseIcon"; +import { mdiInformation, mdiEye, mdiEyeOff } from '@mdi/js'; +import SectionFullScreen from '../components/SectionFullScreen'; +import LayoutGuest from '../layouts/Guest'; +import { Field, Form, Formik } from 'formik'; +import FormField from '../components/FormField'; +import FormCheckRadio from '../components/FormCheckRadio'; +import BaseDivider from '../components/BaseDivider'; +import BaseButtons from '../components/BaseButtons'; +import { useRouter } from 'next/router'; +import { getPageTitle } from '../config'; +import { findMe, loginUser, resetAction } from '../stores/authSlice'; +import { useAppDispatch, useAppSelector } from '../stores/hooks'; +import Link from 'next/link'; +import {toast, ToastContainer} from "react-toastify"; +import { getPexelsImage, getPexelsVideo } from '../helpers/pexels' + +export default function Login() { + const router = useRouter(); + const dispatch = useAppDispatch(); + const textColor = useAppSelector((state) => state.style.linkColor); + const iconsColor = useAppSelector((state) => state.style.iconsColor); + const notify = (type, msg) => toast(msg, { type }); + + const [showPassword, setShowPassword] = useState(false); + const { currentUser, isFetching, errorMessage, token, notify:notifyState } = useAppSelector( + (state) => state.auth, + ); + const [initialValues, setInitialValues] = React.useState({ email:'admin@flatlogic.com', + password: '3b603a71', + remember: true }) + + const title = 'betterp' + + // Fetch user data + useEffect(() => { + if (token) { + dispatch(findMe()); + } + }, [token, dispatch]); + // Redirect to dashboard if user is logged in + useEffect(() => { + if (currentUser?.id) { + router.push('/dashboard'); + } + }, [currentUser?.id, router]); + // Show error message if there is one + useEffect(() => { + if (errorMessage){ + notify('error', errorMessage) + } + + }, [errorMessage]) + // Show notification if there is one + useEffect(() => { + if (notifyState?.showNotification) { + notify('success', notifyState?.textNotification) + dispatch(resetAction()); + } + }, [notifyState?.showNotification]) + + const togglePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + const handleSubmit = async (value) => { + const {remember, ...rest} = value + await dispatch(loginUser(rest)); + }; + + const setLogin = (target) => { + const email = target?.innerText; + setInitialValues(prev => { + return {...prev, email, password: '3b603a71'} + }) + } + + return ( +
    + + {getPageTitle('Login')} + + + +
    +
    + + +

    {title}

    +
    +
    +

    Use{' '} + setLogin(e.target)}>admin@flatlogic.com{' / '} + 3b603a71{' / '} + to login as Admin

    +

    Use setLogin(e.target)}>client@hello.com{' / '} + 9539bcd4f7bb{' / '} + to login as User

    +
    +
    + +
    +
    +
    + + + handleSubmit(values)} + > +
    + + + + +
    + + + +
    + +
    +
    + +
    + + + + + + {'Forgot password?'} + +
    + + + + + + +
    +

    + {'Don’t have an account yet?'}{' '} + + {'New Account'} + +

    + +
    +
    +
    +
    +
    +
    +

    © 2024 {title}. {'© All rights reserved'}

    + + {'Privacy Policy'} + +
    + +
    + ); +} + +Login.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/password-reset.tsx b/frontend/src/pages/password-reset.tsx new file mode 100644 index 0000000..e75c2f4 --- /dev/null +++ b/frontend/src/pages/password-reset.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import type { ReactElement } from 'react'; +import LayoutGuest from '../layouts/Guest'; +import PasswordSetOrReset from '../components/PasswordSetOrReset'; + +export default function Reset() { + return ; +} + +Reset.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/privacy-policy.tsx b/frontend/src/pages/privacy-policy.tsx new file mode 100644 index 0000000..bebd4d6 --- /dev/null +++ b/frontend/src/pages/privacy-policy.tsx @@ -0,0 +1,292 @@ +import React, { useEffect, useState } from 'react'; +import type { ReactElement } from 'react'; +import Head from 'next/head'; +import LayoutGuest from '../layouts/Guest'; +import { getPageTitle } from '../config'; + +export default function PrivacyPolicy() { + const title = 'betterp' + const [projectUrl, setProjectUrl] = useState(''); + + useEffect(() => { + setProjectUrl(location.origin); + }, []); + + const Introduction = () => { + return ( + <> +

    1. Introduction

    +

    + {/* eslint-disable-next-line react/no-unescaped-entities */} + We at {title} ("we", "us", "our") are committed to + protecting your privacy. This Privacy Policy explains how we collect, + use, disclose, and safeguard your information when you visit our + website {projectUrl}, use our services, or + interact with us in other ways. By using our services, you agree to + the collection and use of information in accordance with this policy. +

    + + ); + }; + + const Information = () => { + return ( + <> +

    2. Information We Collect

    +
    +

    2.1 Personal Identification Information

    +

    + We collect various types of personal information in connection with + the services we provide, including: +

    +
      +
    • + Contact Information: Name, email address, phone number, mailing + address. +
    • +
    • Account Information: Username, password, profile picture.
    • +
    • Payment Information: Credit card details, billing address.
    • +
    • Demographic Information: Age, gender, interests.
    • +
    +

    2.2 Technical Data

    +

    + We automatically collect certain information when you visit, use, or + navigate our services. This information may include: +

    +
      +
    • + Device Information: IP address, browser type, operating system, + device type. +
    • +
    • + Usage Data: Pages visited, time spent on each page, links clicked, + and other actions taken on our site. +
    • +
    +

    2.3 Cookies and Tracking Technologies

    +

    + We use cookies and similar tracking technologies to track the + activity on our service and hold certain information. You can + instruct your browser to refuse all cookies or to indicate when a + cookie is being sent. +

    +
    + + ); + }; + + const HowToUser = () => { + return ( + <> +

    3. How We Use Your Information

    +

    We use the information we collect in various ways, including to:

    +
      +
    • Provide, operate, and maintain our website and services.
    • +
    • Improve, personalize, and expand our website and services.
    • +
    • Understand and analyze how you use our website and services.
    • +
    • Develop new products, services, features, and functionality.
    • +
    • + Communicate with you, either directly or through one of our + partners, including for customer service, to provide you with + updates and other information relating to the website, and for + marketing and promotional purposes. +
    • +
    • + Process your transactions and send you related information, + including purchase confirmations and invoices. +
    • +
    • Find and prevent fraud.
    • +
    • Comply with legal obligations.
    • +
    + + ); + }; + + const DataProtection = () => { + return ( + <> +

    4. Data Protection and Security

    +

    + We implement a variety of security measures to maintain the safety of + your personal information. These measures include: +

    +
      +
    • + Encryption: We use encryption to protect sensitive information + transmitted online. Access Controls: We restrict access to your + personal data to authorized personnel only. Regular Security Audits: + We conduct regular audits to identify and address potential security + vulnerabilities. +
    • +
    + + ); + }; + + const Sharing = () => { + return ( + <> +

    5. Sharing Your Information

    +

    + We do not sell, trade, or otherwise transfer your Personally + Identifiable Information to outside parties without your consent, + except in the following cases: +

    +
      +
    • + Service Providers: We may share your information with third-party + service providers who perform services on our behalf, such as + payment processing, data analysis, email delivery, hosting services, + customer service, and marketing assistance. +
    • +
    • + Business Transfers: In the event of a merger, acquisition, or sale + of all or a portion of our assets, your information may be + transferred as part of that transaction. +
    • +
    • + Legal Requirements: We may disclose your information if required to + do so by law or in response to valid requests by public authorities + (e.g., a court or a government agency). +
    • +
    + + ); + }; + + const ProtectionRights = () => { + return ( + <> +

    6. Your Data Protection Rights

    +

    + Depending on your location, you may have the following rights + regarding your personal data: +

    +
      +
    • + The Right to Access: You have the right to request copies of your + personal data. +
    • +
    • + The Right to Rectification: You have the right to request that we + correct any information you believe is inaccurate or complete + information you believe is incomplete. +
    • +
    • + The Right to Erasure: You have the right to request that we erase + your personal data, under certain conditions. +
    • +
    • + The Right to Restrict Processing: You have the right to request that + we restrict the processing of your personal data, under certain + conditions. +
    • +
    • + The Right to Object to Processing: You have the right to object to + our processing of your personal data, under certain conditions. +
    • +
    • + The Right to Data Portability: You have the right to request that we + transfer the data that we have collected to another organization, or + directly to you, under certain conditions. +
    • +
    + + ); + }; + + const DataTransfers = () => { + return ( + <> +

    7. International Data Transfers

    +

    + Your information, including personal data, may be transferred to — and + maintained on — computers located outside of your state, province, + country, or other governmental jurisdiction where the data protection + laws may differ from those of your jurisdiction. We will take all + steps reasonably necessary to ensure that your data is treated + securely and in accordance with this Privacy Policy. +

    + + ); + }; + + const RetentionOfData = () => { + return ( + <> +

    8. Retention of Data

    +

    + We will retain your personal data only for as long as is necessary for + the purposes set out in this Privacy Policy. We will retain and use + your personal data to the extent necessary to comply with our legal + obligations, resolve disputes, and enforce our policies. +

    + + ); + }; + + const ChangePrivacy = () => { + return ( + <> +

    9. Changes to This Privacy Policy

    +

    + We may update our Privacy Policy from time to time. We will notify you + of any changes by posting the new Privacy Policy on this page. You are + advised to review this Privacy Policy periodically for any changes. + Changes to this Privacy Policy are effective when they are posted on + this page. +

    + + ); + }; + + const ContactUs = () => { + return ( + <> +

    10. Contact Us

    +

    + If you have any questions about this Privacy Policy, please contact + us: +

    +
    + By email:{' '} + [support@flatlogic.com] +
    +
    + By visiting this page on our website:{' '} + Contact Us +
    + + ); + }; + + return ( +
    + + {getPageTitle('Privacy Policy')} + + +
    +
    +
    +

    Privacy Policy

    + + + + + + + + + + +
    +
    +
    +
    + ); +} + +PrivacyPolicy.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/profile.tsx b/frontend/src/pages/profile.tsx new file mode 100644 index 0000000..9b2a9a9 --- /dev/null +++ b/frontend/src/pages/profile.tsx @@ -0,0 +1,175 @@ +import { + mdiChartTimelineVariant, + mdiUpload, +} from '@mdi/js'; +import Head from 'next/head'; +import React, { ReactElement, useEffect, useState } from 'react'; +import { ToastContainer, toast } from 'react-toastify'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; + +import CardBox from '../components/CardBox'; +import LayoutAuthenticated from '../layouts/Authenticated'; +import SectionMain from '../components/SectionMain'; +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'; +import { getPageTitle } from '../config'; + +import { Field, Form, Formik } from 'formik'; +import FormField from '../components/FormField'; +import BaseDivider from '../components/BaseDivider'; +import BaseButtons from '../components/BaseButtons'; +import BaseButton from '../components/BaseButton'; +import FormCheckRadio from '../components/FormCheckRadio'; +import FormCheckRadioGroup from '../components/FormCheckRadioGroup'; +import FormImagePicker from '../components/FormImagePicker'; +import { SwitchField } from '../components/SwitchField'; +import { SelectField } from '../components/SelectField'; + +import { update, fetch } from '../stores/users/usersSlice'; +import { useAppDispatch, useAppSelector } from '../stores/hooks'; +import { useRouter } from 'next/router'; +import {findMe} from "../stores/authSlice"; + +const EditUsers = () => { + const { currentUser, isFetching, token } = useAppSelector( + (state) => state.auth, + ); + const router = useRouter(); + const dispatch = useAppDispatch(); + const notify = (type, msg) => toast(msg, { type }); + const initVals = { + firstName: '', + lastName: '', + phoneNumber: '', + email: '', + app_role: '', + disabled: false, + avatar: [], + password: '' + }; + const [initialValues, setInitialValues] = useState(initVals); + + useEffect(() => { + if (currentUser?.id && typeof currentUser === 'object') { + const newInitialVal = { ...initVals }; + + Object.keys(initVals).forEach( + (el) => (newInitialVal[el] = currentUser[el]), + ); + + setInitialValues(newInitialVal); + } + }, [currentUser]); + + const handleSubmit = async (data) => { + await dispatch(update({ id: currentUser.id, data })); + await dispatch(findMe()); + await router.push('/users/users-list'); + notify('success', 'Profile was updated!'); + }; + + return ( + <> + + {getPageTitle('Edit profile')} + + + + {''} + + + handleSubmit(values)} + > +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + router.push('/users/users-list')} + /> + + +
    +
    +
    + + ); +}; + +EditUsers.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default EditUsers; diff --git a/frontend/src/pages/register.tsx b/frontend/src/pages/register.tsx new file mode 100644 index 0000000..bc39ab3 --- /dev/null +++ b/frontend/src/pages/register.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import type { ReactElement } from 'react'; +import { ToastContainer, toast } from 'react-toastify'; +import Head from 'next/head'; +import BaseButton from '../components/BaseButton'; +import CardBox from '../components/CardBox'; +import SectionFullScreen from '../components/SectionFullScreen'; +import LayoutGuest from '../layouts/Guest'; +import { Field, Form, Formik } from 'formik'; +import FormField from '../components/FormField'; +import BaseDivider from '../components/BaseDivider'; +import BaseButtons from '../components/BaseButtons'; +import { useRouter } from 'next/router'; +import { getPageTitle } from '../config'; +import Select from 'react-select'; +import axios from "axios"; + +export default function Register() { + const [loading, setLoading] = React.useState(false); + const router = useRouter(); + const notify = (type, msg) => toast( msg, {type, position: "bottom-center"}); + + const handleSubmit = async (value) => { + setLoading(true) + try { + const { data: response } = await axios.post('/auth/signup', value); + await router.push('/login') + setLoading(false) + notify('success', 'Please check your email for verification link') + } catch (error) { + setLoading(false) + console.log('error: ', error) + notify('error', 'Something was wrong. Try again') + } + }; + + return ( + <> + + {getPageTitle('Login')} + + + + + handleSubmit(values)} + > +
    + + + + + + + + + + + + + + + + + +
    +
    +
    + + + ); +} + +Register.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/search.tsx b/frontend/src/pages/search.tsx new file mode 100644 index 0000000..315b9cc --- /dev/null +++ b/frontend/src/pages/search.tsx @@ -0,0 +1,86 @@ +import React, { ReactElement, useEffect, useState } from 'react'; +import Head from 'next/head'; +import 'react-datepicker/dist/react-datepicker.css'; +import { useAppDispatch } from '../stores/hooks'; +import { useRouter } from 'next/router'; +import LayoutAuthenticated from '../layouts/Authenticated'; +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'; +import SectionMain from '../components/SectionMain'; +import CardBox from '../components/CardBox'; +import SearchResults from '../components/SearchResults'; +import LoadingSpinner from '../components/LoadingSpinner'; +import BaseButton from '../components/BaseButton'; +import BaseDivider from '../components/BaseDivider'; +import { mdiChartTimelineVariant } from '@mdi/js'; +import { createAsyncThunk } from '@reduxjs/toolkit'; +import axios from 'axios'; + +const SearchView = () => { + const router = useRouter(); + const dispatch = useAppDispatch(); + const searchQuery = router.query.query; + const [loading, setLoading] = useState(false); + const [searchResults, setSearchResults] = useState([]); + + useEffect(() => { + dispatch(fetchData()); + }, [dispatch, searchQuery]); + + const fetchData = createAsyncThunk('/search', async () => { + setLoading(true); + try { + const response = await axios.post('/search', { searchQuery }); + setSearchResults(response.data); + setLoading(false); + return response.data; + } catch (error) { + console.error(error.response); + setLoading(false); + throw error; + } + }); + + const groupedResults = searchResults.reduce((acc, item) => { + const { tableName } = item; + acc[tableName] = acc[tableName] || []; + acc[tableName].push(item); + return acc; + }, {}); + + return ( + <> + + Search Result + + + + {''} + + + {loading ? : } + + router.push('/dashboard')} + /> + + + + ); +}; + +SearchView.getLayout = function getLayout(page: ReactElement) { + return ( + {page} + ); +}; + +export default SearchView; diff --git a/frontend/src/pages/tables.tsx b/frontend/src/pages/tables.tsx new file mode 100644 index 0000000..42c7c61 --- /dev/null +++ b/frontend/src/pages/tables.tsx @@ -0,0 +1,33 @@ +import {mdiChartTimelineVariant} from '@mdi/js' +import Head from 'next/head' +import React, { ReactElement } from 'react' +import CardBox from '../components/CardBox' +import LayoutAuthenticated from '../layouts/Authenticated' +import SectionMain from '../components/SectionMain' +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' +import TableSampleClients from '../components/TableSampleClients' +import { getPageTitle } from '../config' + +const TablesPage = () => { + return ( + <> + + {getPageTitle('Tables')} + + + + {''} + + + + + + + ) +} + +TablesPage.getLayout = function getLayout(page: ReactElement) { + return {page} +} + +export default TablesPage diff --git a/frontend/src/pages/terms-of-use.tsx b/frontend/src/pages/terms-of-use.tsx new file mode 100644 index 0000000..e57abe8 --- /dev/null +++ b/frontend/src/pages/terms-of-use.tsx @@ -0,0 +1,206 @@ +import React, { useEffect, useState } from 'react'; +import type { ReactElement } from 'react'; +import Head from 'next/head'; +import LayoutGuest from '../layouts/Guest'; +import { getPageTitle } from '../config'; + +export default function PrivacyPolicy() { + const title = 'betterp'; + const [projectUrl, setProjectUrl] = useState(''); + + useEffect(() => { + setProjectUrl(location.origin); + }, []); + + const Information = () => { + return ( + <> +

    1. Acceptance of Terms

    +
    +

    + By accessing and using our application, you agree to comply with and + be bound by these Terms of Use. If you do not agree to these terms, + please do not use the application. +

    +
    + + ); + }; + + const ChangesTerms = () => { + return ( + <> +

    2. Changes to Terms

    +

    + We reserve the right to modify these Terms of Use at any time. Any + changes will be effective immediately upon posting. Your continued use + of the application after any such changes constitutes your acceptance + of the new terms. +

    + + ); + }; + + const UseApplication = () => { + return ( + <> +

    3. Use of the Application

    +

    + You agree to use the application only for lawful purposes and in a way + that does not infringe the rights of, restrict, or inhibit anyone + else’s use and enjoyment of the application. Prohibited behavior + includes harassing or causing distress or inconvenience to any other + user, transmitting obscene or offensive content, or disrupting the + normal flow of dialogue within the application. +

    + + ); + }; + + const IntellectualProperty = () => { + return ( + <> +

    4. Intellectual Property

    +

    + All content included on the application, such as text, graphics, + logos, images, and software, is the property of {title} or + its content suppliers and protected by international copyright laws. + Unauthorized use of the content may violate copyright, trademark, and + other laws. +

    + + ); + }; + + const UserContent = () => { + return ( + <> +

    5. User Content

    +

    + You are responsible for any content you upload, post, or otherwise + make available through the application. You grant {title} + a worldwide, irrevocable, non-exclusive, royalty-free license to use, + reproduce, modify, publish, and distribute such content for any + purpose. +

    + + ); + }; + + const Privacy = () => { + return ( + <> +

    6. Privacy

    +

    + Your privacy is important to us. Please review our Privacy Policy to + understand our practices regarding the collection, use, and disclosure + of your personal information. +

    + + ); + }; + + const Liability = () => { + return ( + <> +

    7. Limitation of Liability

    +

    + The application is provided “as is” and “as available” without any + warranties of any kind, either express or implied. {title} + does not warrant that the application will be uninterrupted or + error-free. In no event shall {title} be liable for any + damages arising out of your use of the application. +

    + + ); + }; + + const Indemnification = () => { + return ( + <> +

    8. Indemnification

    +

    + You agree to indemnify, defend, and hold harmless {title}, + its officers, directors, employees, and agents from and against any + claims, liabilities, damages, losses, and expenses, including without + limitation reasonable legal and accounting fees, arising out of or in + any way connected with your access to or use of the application or + your violation of these Terms of Use. +

    + + ); + }; + + const Termination = () => { + return ( + <> +

    9. Termination

    +

    + We reserve the right to terminate or suspend your access to the + application at our sole discretion, without notice and without + liability, for any reason, including if we believe you have violated + these Terms of Use. +

    + + ); + }; + + const GoverningLaw = () => { + return ( + <> +

    10. Governing Law

    +

    + These Terms of Use are governed by and interpreted in accordance with + applicable laws, without regard to any conflict of law principles. You + agree to submit to the exclusive jurisdiction of the courts that have + authority to resolve any dispute arising from the use of the + application. +

    + + ); + }; + + const ContactUs = () => { + return ( + <> +

    11. Contact Information

    +

    + If you have any questions about these Terms of Use, please contact us + at: [support@flatlogic.com] +

    + + ); + }; + + return ( +
    + + {getPageTitle('Terms of Use')} + + +
    +
    +
    +

    Terms of Use

    + + + + + + + + + + + + +
    +
    +
    +
    + ); +} + +PrivacyPolicy.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/pages/users/[usersId].tsx b/frontend/src/pages/users/[usersId].tsx new file mode 100644 index 0000000..a8be6aa --- /dev/null +++ b/frontend/src/pages/users/[usersId].tsx @@ -0,0 +1,172 @@ +import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js' +import Head from 'next/head' +import React, { ReactElement, useEffect, useState } from 'react' +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import dayjs from "dayjs"; + +import CardBox from '../../components/CardBox' +import LayoutAuthenticated from '../../layouts/Authenticated' +import SectionMain from '../../components/SectionMain' +import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' +import { getPageTitle } from '../../config' + +import { Field, Form, Formik } from 'formik' +import FormField from '../../components/FormField' +import BaseDivider from '../../components/BaseDivider' +import BaseButtons from '../../components/BaseButtons' +import BaseButton from '../../components/BaseButton' +import FormCheckRadio from '../../components/FormCheckRadio' +import FormCheckRadioGroup from '../../components/FormCheckRadioGroup' +import { SelectField } from "../../components/SelectField"; +import { SelectFieldMany } from "../../components/SelectFieldMany"; +import { SwitchField } from '../../components/SwitchField' +import {RichTextField} from "../../components/RichTextField"; + +import { update, fetch } from '../../stores/users/usersSlice' +import { useAppDispatch, useAppSelector } from '../../stores/hooks' +import { useRouter } from 'next/router' + +const EditUsers = () => { + const router = useRouter() + const dispatch = useAppDispatch() + const initVals = { + + 'firstName': '', + + 'lastName': '', + + 'phoneNumber': '', + + 'email': '', + + disabled: false, + + password: '' + + } + const [initialValues, setInitialValues] = useState(initVals) + + const { users } = useAppSelector((state) => state.users) + + const { usersId } = router.query + + useEffect(() => { + dispatch(fetch({ id: usersId })) + }, [usersId]) + + useEffect(() => { + if (typeof users === 'object') { + setInitialValues(users) + } + }, [users]) + + useEffect(() => { + if (typeof users === 'object') { + + const newInitialVal = {...initVals}; + + Object.keys(initVals).forEach(el => newInitialVal[el] = (users)[el]) + + setInitialValues(newInitialVal); + } + }, [users]) + + const handleSubmit = async (data) => { + await dispatch(update({ id: usersId, data })) + await router.push('/users/users-list') + } + + return ( + <> + + {getPageTitle('Edit users')} + + + + {''} + + + handleSubmit(values)} + > +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + router.push('/users/users-list')}/> + + +
    +
    +
    + + ) +} + +EditUsers.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default EditUsers diff --git a/frontend/src/pages/users/users-edit.tsx b/frontend/src/pages/users/users-edit.tsx new file mode 100644 index 0000000..97158a0 --- /dev/null +++ b/frontend/src/pages/users/users-edit.tsx @@ -0,0 +1,170 @@ +import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js' +import Head from 'next/head' +import React, { ReactElement, useEffect, useState } from 'react' +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import dayjs from "dayjs"; + +import CardBox from '../../components/CardBox' +import LayoutAuthenticated from '../../layouts/Authenticated' +import SectionMain from '../../components/SectionMain' +import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' +import { getPageTitle } from '../../config' + +import { Field, Form, Formik } from 'formik' +import FormField from '../../components/FormField' +import BaseDivider from '../../components/BaseDivider' +import BaseButtons from '../../components/BaseButtons' +import BaseButton from '../../components/BaseButton' +import FormCheckRadio from '../../components/FormCheckRadio' +import FormCheckRadioGroup from '../../components/FormCheckRadioGroup' +import { SelectField } from "../../components/SelectField"; +import { SelectFieldMany } from "../../components/SelectFieldMany"; +import { SwitchField } from '../../components/SwitchField' +import {RichTextField} from "../../components/RichTextField"; + +import { update, fetch } from '../../stores/users/usersSlice' +import { useAppDispatch, useAppSelector } from '../../stores/hooks' +import { useRouter } from 'next/router' +import dataFormatter from '../../helpers/dataFormatter'; + +const EditUsersPage = () => { + const router = useRouter() + const dispatch = useAppDispatch() + const initVals = { + + 'firstName': '', + + 'lastName': '', + + 'phoneNumber': '', + + 'email': '', + + disabled: false, + + password: '' + + } + const [initialValues, setInitialValues] = useState(initVals) + + const { users } = useAppSelector((state) => state.users) + + const { id } = router.query + + useEffect(() => { + dispatch(fetch({ id: id })) + }, [id]) + + useEffect(() => { + if (typeof users === 'object') { + setInitialValues(users) + } + }, [users]) + + useEffect(() => { + if (typeof users === 'object') { + const newInitialVal = {...initVals}; + Object.keys(initVals).forEach(el => newInitialVal[el] = (users)[el]) + setInitialValues(newInitialVal); + } + }, [users]) + + const handleSubmit = async (data) => { + await dispatch(update({ id: id, data })) + await router.push('/users/users-list') + } + + return ( + <> + + {getPageTitle('Edit users')} + + + + {''} + + + handleSubmit(values)} + > +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + router.push('/users/users-list')}/> + + +
    +
    +
    + + ) +} + +EditUsersPage.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default EditUsersPage diff --git a/frontend/src/pages/users/users-list.tsx b/frontend/src/pages/users/users-list.tsx new file mode 100644 index 0000000..6731c12 --- /dev/null +++ b/frontend/src/pages/users/users-list.tsx @@ -0,0 +1,128 @@ +import { mdiChartTimelineVariant } from '@mdi/js' +import Head from 'next/head' +import { uniqueId } from 'lodash'; +import React, { ReactElement, useState } from 'react' +import CardBox from '../../components/CardBox' +import LayoutAuthenticated from '../../layouts/Authenticated' +import SectionMain from '../../components/SectionMain' +import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' +import { getPageTitle } from '../../config' +import TableUsers from '../../components/Users/TableUsers' +import BaseButton from '../../components/BaseButton' +import axios from "axios"; +import {useAppDispatch, useAppSelector} from "../../stores/hooks"; +import CardBoxModal from "../../components/CardBoxModal"; +import DragDropFilePicker from "../../components/DragDropFilePicker"; +import {setRefetch, uploadCsv} from '../../stores/users/usersSlice'; + +const UsersTablesPage = () => { + const [filterItems, setFilterItems] = useState([]); + const [csvFile, setCsvFile] = useState(null); + const [isModalActive, setIsModalActive] = useState(false); + + const dispatch = useAppDispatch(); + + const [filters] = useState([{label: 'First Name', title: 'firstName'},{label: 'Last Name', title: 'lastName'},{label: 'Phone Number', title: 'phoneNumber'},{label: 'E-Mail', title: 'email'}, + + ]); + const addFilter = () => { + const newItem = { + id: uniqueId(), + fields: { + filterValue: '', + filterValueFrom: '', + filterValueTo: '', + selectedField: '', + }, + }; + newItem.fields.selectedField = filters[0].title; + setFilterItems([...filterItems, newItem]); + }; + + const getUsersCSV = async () => { + const response = await axios({url: '/users?filetype=csv', method: 'GET',responseType: 'blob'}); + const type = response.headers['content-type'] + const blob = new Blob([response.data], { type: type }) + const link = document.createElement('a') + link.href = window.URL.createObjectURL(blob) + link.download = 'usersCSV.csv' + link.click() + }; + + const onModalConfirm = async () => { + if (!csvFile) return; + await dispatch(uploadCsv(csvFile)); + dispatch(setRefetch(true)); + setCsvFile(null); + setIsModalActive(false); + }; + + const onModalCancel = () => { + setCsvFile(null); + setIsModalActive(false); + }; + + return ( + <> + + {getPageTitle('Users')} + + + + {''} + + + + + + setIsModalActive(true)} + /> +
    +
    +
    +
    + + + +
    + + + + + ) +} + +UsersTablesPage.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default UsersTablesPage diff --git a/frontend/src/pages/users/users-new.tsx b/frontend/src/pages/users/users-new.tsx new file mode 100644 index 0000000..ac3ca0e --- /dev/null +++ b/frontend/src/pages/users/users-new.tsx @@ -0,0 +1,132 @@ +import { mdiChartTimelineVariant } from '@mdi/js' +import Head from 'next/head' +import React, { ReactElement } from 'react' +import CardBox from '../../components/CardBox' +import LayoutAuthenticated from '../../layouts/Authenticated' +import SectionMain from '../../components/SectionMain' +import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' +import { getPageTitle } from '../../config' + +import { Field, Form, Formik } from 'formik' +import FormField from '../../components/FormField' +import BaseDivider from '../../components/BaseDivider' +import BaseButtons from '../../components/BaseButtons' +import BaseButton from '../../components/BaseButton' +import FormCheckRadio from '../../components/FormCheckRadio' +import FormCheckRadioGroup from '../../components/FormCheckRadioGroup' +import { SwitchField } from '../../components/SwitchField' + +import { SelectField } from '../../components/SelectField' +import {RichTextField} from "../../components/RichTextField"; + +import { create } from '../../stores/users/usersSlice' +import { useAppDispatch } from '../../stores/hooks' +import { useRouter } from 'next/router' + +const initialValues = { + + firstName: '', + + lastName: '', + + phoneNumber: '', + + email: '', + + disabled: false, + +} + +const UsersNew = () => { + const router = useRouter() + const dispatch = useAppDispatch() + + const handleSubmit = async (data) => { + await dispatch(create(data)) + await router.push('/users/users-list') + } + return ( + <> + + {getPageTitle('New Item')} + + + + {''} + + + handleSubmit(values)} + > +
    + + + + + + + + + + + + + + + + + + + + + + + + + + router.push('/users/users-list')}/> + + +
    +
    +
    + + ) +} + +UsersNew.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default UsersNew diff --git a/frontend/src/pages/users/users-table.tsx b/frontend/src/pages/users/users-table.tsx new file mode 100644 index 0000000..e411807 --- /dev/null +++ b/frontend/src/pages/users/users-table.tsx @@ -0,0 +1,129 @@ +import { mdiChartTimelineVariant } from '@mdi/js' +import Head from 'next/head' +import { uniqueId } from 'lodash'; +import React, { ReactElement, useState } from 'react' +import CardBox from '../../components/CardBox' +import LayoutAuthenticated from '../../layouts/Authenticated' +import SectionMain from '../../components/SectionMain' +import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' +import { getPageTitle } from '../../config' +import TableUsers from '../../components/Users/TableUsers' +import BaseButton from '../../components/BaseButton' +import axios from "axios"; +import {useAppDispatch, useAppSelector} from "../../stores/hooks"; +import CardBoxModal from "../../components/CardBoxModal"; +import DragDropFilePicker from "../../components/DragDropFilePicker"; +import {setRefetch, uploadCsv} from '../../stores/users/usersSlice'; + +const UsersTablesPage = () => { + const [filterItems, setFilterItems] = useState([]); + const [csvFile, setCsvFile] = useState(null); + const [isModalActive, setIsModalActive] = useState(false); + + const dispatch = useAppDispatch(); + + const [filters] = useState([{label: 'First Name', title: 'firstName'},{label: 'Last Name', title: 'lastName'},{label: 'Phone Number', title: 'phoneNumber'},{label: 'E-Mail', title: 'email'}, + + ]); + const addFilter = () => { + const newItem = { + id: uniqueId(), + fields: { + filterValue: '', + filterValueFrom: '', + filterValueTo: '', + selectedField: '', + }, + }; + newItem.fields.selectedField = filters[0].title; + setFilterItems([...filterItems, newItem]); + }; + + const getUsersCSV = async () => { + const response = await axios({url: '/users?filetype=csv', method: 'GET',responseType: 'blob'}); + const type = response.headers['content-type'] + const blob = new Blob([response.data], { type: type }) + const link = document.createElement('a') + link.href = window.URL.createObjectURL(blob) + link.download = 'usersCSV.csv' + link.click() + }; + + const onModalConfirm = async () => { + if (!csvFile) return; + await dispatch(uploadCsv(csvFile)); + dispatch(setRefetch(true)); + setCsvFile(null); + setIsModalActive(false); + }; + + const onModalCancel = () => { + setCsvFile(null); + setIsModalActive(false); + }; + + return ( + <> + + {getPageTitle('Users')} + + + + {''} + + + + + + + setIsModalActive(true)} + /> +
    +
    +
    +
    + + + +
    + + + + + ) +} + +UsersTablesPage.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default UsersTablesPage diff --git a/frontend/src/pages/users/users-view.tsx b/frontend/src/pages/users/users-view.tsx new file mode 100644 index 0000000..66198b0 --- /dev/null +++ b/frontend/src/pages/users/users-view.tsx @@ -0,0 +1,101 @@ +import React, { ReactElement, useEffect } from 'react'; +import Head from 'next/head' +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import dayjs from "dayjs"; +import {useAppDispatch, useAppSelector} from "../../stores/hooks"; +import {useRouter} from "next/router"; +import { fetch } from '../../stores/users/usersSlice' +import dataFormatter from '../../helpers/dataFormatter'; +import LayoutAuthenticated from "../../layouts/Authenticated"; +import {getPageTitle} from "../../config"; +import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton"; +import SectionMain from "../../components/SectionMain"; +import CardBox from "../../components/CardBox"; +import BaseButton from "../../components/BaseButton"; +import BaseDivider from "../../components/BaseDivider"; +import {mdiChartTimelineVariant} from "@mdi/js"; +import {SwitchField} from "../../components/SwitchField"; +import FormField from "../../components/FormField"; + +const UsersView = () => { + const router = useRouter() + const dispatch = useAppDispatch() + const { users } = useAppSelector((state) => state.users) + + const { id } = router.query; + + function removeLastCharacter(str) { + console.log(str,`str`) + return str.slice(0, -1); + } + + useEffect(() => { + dispatch(fetch({ id })); + }, [dispatch, id]); + + return ( + <> + + {getPageTitle('View users')} + + + + + + + +
    +

    First Name

    +

    {users?.firstName}

    +
    + +
    +

    Last Name

    +

    {users?.lastName}

    +
    + +
    +

    Phone Number

    +

    {users?.phoneNumber}

    +
    + +
    +

    E-Mail

    +

    {users?.email}

    +
    + + + null}} + disabled + /> + + + + + router.push('/users/users-list')} + /> +
    +
    + + ); +}; + +UsersView.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default UsersView; diff --git a/frontend/src/pages/verify-email.tsx b/frontend/src/pages/verify-email.tsx new file mode 100644 index 0000000..6cf0340 --- /dev/null +++ b/frontend/src/pages/verify-email.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import type { ReactElement } from 'react'; +import { ToastContainer, toast } from 'react-toastify'; +import Head from 'next/head'; +import CardBox from '../components/CardBox'; +import SectionFullScreen from '../components/SectionFullScreen'; +import LayoutGuest from '../layouts/Guest'; +import { useRouter } from 'next/router'; +import { getPageTitle } from '../config'; +import axios from 'axios'; + +export default function Verify() { + const [loading, setLoading] = React.useState(false); + const router = useRouter(); + const { token } = router.query; + const notify = (type, msg) => toast(msg, { type }); + + React.useEffect(() => { + if (!token) { + router.push('/login'); + return; + } + const handleSubmit = async () => { + + setLoading(true); + await axios.put('/auth/verify-email', { + token, + }).then(verified => { + if (verified) { + setLoading(false); + notify('success', 'Your email was verified'); + } + }).catch(error => { + setLoading(false); + console.log('error: ', error); + notify('error', error.response); + }).finally(async () => { + await router.push('/login'); + }); + }; + handleSubmit().then(); + }, [token]); + + return ( + <> + + {getPageTitle('Verify Email')} + + + +

    {loading ? 'Loading...' : ''}

    +
    +
    + + + + ); +} + +Verify.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/frontend/src/stores/authSlice.ts b/frontend/src/stores/authSlice.ts new file mode 100644 index 0000000..2eaa1f2 --- /dev/null +++ b/frontend/src/stores/authSlice.ts @@ -0,0 +1,124 @@ +import { createSlice, createAsyncThunk, createAction } from '@reduxjs/toolkit'; +import axios from 'axios'; +import jwt from 'jsonwebtoken'; + +interface MainState { + isFetching: boolean; + errorMessage: string; + currentUser: any; + notify: any; + token: string; +} + +const initialState: MainState = { + /* User */ + isFetching: false, + errorMessage: '', + currentUser: null, + token: '', + notify: { + showNotification: false, + textNotification: '', + typeNotification: 'warn', + }, +}; + +export const resetAction = createAction('auth/passwordReset/reset') + +export const loginUser = createAsyncThunk( + 'auth/loginUser', + async (creds: Record, { rejectWithValue }) => { + try { + const response = await axios.post('auth/signin/local', creds); + return response.data; + } catch (error) { + if (!error.response) { + throw error; + } + return rejectWithValue(error.response.data); + } + } +); + +export const passwordReset = createAsyncThunk( + 'auth/passwordReset', + async (value: Record, { rejectWithValue }) => { + try { + const { data: response } = await axios.put('/auth/password-reset', { + token: value.token, + password: value.password, + type: value.type, + }); + + return response.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const findMe = createAsyncThunk('auth/findMe', async () => { + const response = await axios.get('auth/me'); + return response.data; +}); + +export const authSlice = createSlice({ + name: 'auth', + initialState, + reducers: { + logoutUser: (state) => { + localStorage.removeItem('token'); + localStorage.removeItem('user'); + axios.defaults.headers.common['Authorization'] = ''; + state.currentUser = null; + state.token = ''; + }, + }, + extraReducers: (builder) => { + builder.addCase(loginUser.pending, (state) => { + state.isFetching = true; + }); + builder.addCase(loginUser.fulfilled, (state, action) => { + const token = action.payload; + const user = jwt.decode(token); + + state.errorMessage = ''; + state.token = token; + localStorage.setItem('token', token); + localStorage.setItem('user', JSON.stringify(user)); + axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; + }); + + builder.addCase(loginUser.rejected, (state, action) => { + state.errorMessage = String(action.payload) || 'Something went wrong. Try again'; + state.isFetching = false; + }); + builder.addCase(findMe.pending, () => { + console.log('Pending findMe'); + }); + builder.addCase(findMe.fulfilled, (state, action) => { + state.currentUser = action.payload; + state.isFetching = false; + }); + + builder.addCase(passwordReset.fulfilled, (state, action) => { + state.notify.showNotification = true; + state.notify.textNotification = 'Password has been reset successfully'; + }); + + builder.addCase(resetAction, (state) => initialState); + + builder.addCase(passwordReset.rejected, (state) => { + state.errorMessage = 'Something was wrong. Try again'; + }); + }, +}); + +// Action creators are generated for each case reducer function +export const { logoutUser } = authSlice.actions; + +export default authSlice.reducer; diff --git a/frontend/src/stores/hooks.ts b/frontend/src/stores/hooks.ts new file mode 100644 index 0000000..60a4163 --- /dev/null +++ b/frontend/src/stores/hooks.ts @@ -0,0 +1,6 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' +import type { RootState, AppDispatch } from './store' + +// Use throughout your app instead of plain `useDispatch` and `useSelector` +export const useAppDispatch: () => AppDispatch = useDispatch +export const useAppSelector: TypedUseSelectorHook = useSelector diff --git a/frontend/src/stores/mainSlice.ts b/frontend/src/stores/mainSlice.ts new file mode 100644 index 0000000..4e026b8 --- /dev/null +++ b/frontend/src/stores/mainSlice.ts @@ -0,0 +1,36 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { UserPayloadObject } from '../interfaces' + +interface MainState { + userName: string + userEmail: null | string + userAvatar: null | string + isFieldFocusRegistered: boolean +} + +const initialState: MainState = { + /* User */ + userName: '', + userEmail: null, + userAvatar: null, + + /* Field focus with ctrl+k (to register only once) */ + isFieldFocusRegistered: false, +} + +export const mainSlice = createSlice({ + name: 'main', + initialState, + reducers: { + setUser: (state, action: PayloadAction) => { + state.userName = action.payload.name + state.userEmail = action.payload.email + state.userAvatar = action.payload.avatar + }, + }, +}) + +// Action creators are generated for each case reducer function +export const { setUser } = mainSlice.actions + +export default mainSlice.reducer diff --git a/frontend/src/stores/store.ts b/frontend/src/stores/store.ts new file mode 100644 index 0000000..91d5ae4 --- /dev/null +++ b/frontend/src/stores/store.ts @@ -0,0 +1,21 @@ +import { configureStore } from '@reduxjs/toolkit'; +import styleReducer from './styleSlice'; +import mainReducer from './mainSlice'; +import authSlice from './authSlice'; + +import usersSlice from "./users/usersSlice"; + +export const store = configureStore({ + reducer: { + style: styleReducer, + main: mainReducer, + auth: authSlice, + +users: usersSlice, + }, +}) + +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType +// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} +export type AppDispatch = typeof store.dispatch diff --git a/frontend/src/stores/styleSlice.ts b/frontend/src/stores/styleSlice.ts new file mode 100644 index 0000000..89eac7f --- /dev/null +++ b/frontend/src/stores/styleSlice.ts @@ -0,0 +1,101 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import * as styles from '../styles' +import { localStorageDarkModeKey, localStorageStyleKey } from '../config' +import { StyleKey } from '../interfaces' + +interface StyleState { + asideStyle: string + asideScrollbarsStyle: string + asideBrandStyle: string + asideMenuItemStyle: string + asideMenuItemActiveStyle: string + asideMenuDropdownStyle: string + navBarItemLabelStyle: string + navBarItemLabelHoverStyle: string + navBarItemLabelActiveColorStyle: string + overlayStyle: string + darkMode: boolean + bgLayoutColor: string; + iconsColor: string; + activeLinkColor: string; + cardsColor: string; + focusRingColor: string; + corners: string; + cardsStyle: string; + linkColor: string; + websiteHeder: string; + borders: string; + shadow: string; + websiteSectionStyle: string; + textSecondary: string; +} + +const initialState: StyleState = { + asideStyle: styles.white.aside, + asideScrollbarsStyle: styles.white.asideScrollbars, + asideBrandStyle: styles.white.asideBrand, + asideMenuItemStyle: styles.white.asideMenuItem, + asideMenuItemActiveStyle: styles.white.asideMenuItemActive, + asideMenuDropdownStyle: styles.white.asideMenuDropdown, + navBarItemLabelStyle: styles.white.navBarItemLabel, + navBarItemLabelHoverStyle: styles.white.navBarItemLabelHover, + navBarItemLabelActiveColorStyle: styles.white.navBarItemLabelActiveColor, + overlayStyle: styles.white.overlay, + darkMode: false, + bgLayoutColor: styles.white.bgLayoutColor, + iconsColor: styles.white.iconsColor, + activeLinkColor: styles.white.activeLinkColor, + cardsColor: styles.white.cardsColor, + focusRingColor: styles.white.focusRingColor, + corners: styles.white.corners, + cardsStyle: styles.white.cardsStyle, + linkColor: styles.white.linkColor, + websiteHeder: styles.white.websiteHeder, + borders: styles.white.borders, + shadow: styles.white.shadow, + websiteSectionStyle: styles.white.websiteSectionStyle, + textSecondary: styles.white.textSecondary, +} + +export const styleSlice = createSlice({ + name: 'style', + initialState, + reducers: { + setDarkMode: (state, action: PayloadAction) => { + state.darkMode = action.payload !== null ? action.payload : !state.darkMode + + if (typeof localStorage !== 'undefined') { + localStorage.setItem(localStorageDarkModeKey, state.darkMode ? '1' : '0') + } + + if (typeof document !== 'undefined') { + document.body.classList[state.darkMode ? 'add' : 'remove']('dark-scrollbars') + + document.documentElement.classList[state.darkMode ? 'add' : 'remove']( + 'dark-scrollbars-compat' + ) + } + }, + + setStyle: (state, action: PayloadAction) => { + if (!styles[action.payload]) { + return + } + + if (typeof localStorage !== 'undefined') { + localStorage.setItem(localStorageStyleKey, action.payload) + } + + const style = styles[action.payload] + + for (const key in style) { + state[`${key}Style`] = style[key] + } + }, + }, +}) + +// Action creators are generated for each case reducer function +export const { setDarkMode, setStyle } = styleSlice.actions + +export default styleSlice.reducer diff --git a/frontend/src/stores/users/usersSlice.ts b/frontend/src/stores/users/usersSlice.ts new file mode 100644 index 0000000..0d405e8 --- /dev/null +++ b/frontend/src/stores/users/usersSlice.ts @@ -0,0 +1,229 @@ +import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit' +import axios from 'axios' +import {fulfilledNotify, rejectNotify, resetNotify} from "../../helpers/notifyStateHandler"; + +interface MainState { + users: any + loading: boolean + count: number + refetch: boolean; + rolesWidgets: any[]; + notify: { + showNotification: boolean + textNotification: string + typeNotification: string + } +} + +const initialState: MainState = { + users: [], + loading: false, + count: 0, + refetch: false, + rolesWidgets: [], + notify: { + showNotification: false, + textNotification: '', + typeNotification: 'warn', + }, +} + +export const fetch = createAsyncThunk('users/fetch', async (data: any) => { + const { id, query } = data + const result = await axios.get( + `users${ + query || (id ? `/${id}` : '') + }` + ) + return id ? result.data : {rows: result.data.rows, count: result.data.count}; +}) + +export const deleteItemsByIds = createAsyncThunk( + 'users/deleteByIds', + async (data: any, { rejectWithValue }) => { + try { + await axios.post('users/deleteByIds', { data }); + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const deleteItem = createAsyncThunk('users/deleteUsers', async (id: string, { rejectWithValue }) => { + try { + await axios.delete(`users/${id}`) + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } +}) + +export const create = createAsyncThunk('users/createUsers', async (data: any, { rejectWithValue }) => { + try { + const result = await axios.post( + 'users', + { data } + ) + return result.data + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } +}) + +export const uploadCsv = createAsyncThunk( + 'users/uploadCsv', + async (file: File, { rejectWithValue }) => { + try { + const data = new FormData(); + data.append('file', file); + data.append('filename', file.name); + + const result = await axios.post('users/bulk-import', data, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + + return result.data; + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } + }, +); + +export const update = createAsyncThunk('users/updateUsers', async (payload: any, { rejectWithValue }) => { + try { + const result = await axios.put( + `users/${payload.id}`, + { id: payload.id, data: payload.data } + ) + return result.data + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } +}) + +export const usersSlice = createSlice({ + name: 'users', + initialState, + reducers: { + setRefetch: (state, action: PayloadAction) => { + state.refetch = action.payload; + }, + }, + extraReducers: (builder) => { + builder.addCase(fetch.pending, (state) => { + state.loading = true + resetNotify(state); + }) + builder.addCase(fetch.rejected, (state, action) => { + state.loading = false + rejectNotify(state, action); + }) + + builder.addCase(fetch.fulfilled, (state, action) => { + if (action.payload.rows && action.payload.count >= 0) { + state.users = action.payload.rows; + state.count = action.payload.count; + } else { + state.users = action.payload; + } + state.loading = false + }) + + builder.addCase(deleteItemsByIds.pending, (state) => { + state.loading = true; + resetNotify(state); + }); + + builder.addCase(deleteItemsByIds.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Users has been deleted'); + }); + + builder.addCase(deleteItemsByIds.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }); + + builder.addCase(deleteItem.pending, (state) => { + state.loading = true + resetNotify(state); + }) + + builder.addCase(deleteItem.fulfilled, (state) => { + state.loading = false + fulfilledNotify(state, `${'Users'.slice(0, -1)} has been deleted`); + }) + + builder.addCase(deleteItem.rejected, (state, action) => { + state.loading = false + rejectNotify(state, action); + }) + + builder.addCase(create.pending, (state) => { + state.loading = true + resetNotify(state); + }) + builder.addCase(create.rejected, (state, action) => { + state.loading = false + rejectNotify(state, action); + }) + + builder.addCase(create.fulfilled, (state) => { + state.loading = false + fulfilledNotify(state, `${'Users'.slice(0, -1)} has been created`); + }) + + builder.addCase(update.pending, (state) => { + state.loading = true + resetNotify(state); + }) + builder.addCase(update.fulfilled, (state) => { + state.loading = false + fulfilledNotify(state, `${'Users'.slice(0, -1)} has been updated`); + }) + builder.addCase(update.rejected, (state, action) => { + state.loading = false + rejectNotify(state, action); + }) + + builder.addCase(uploadCsv.pending, (state) => { + state.loading = true; + resetNotify(state); + }) + builder.addCase(uploadCsv.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Users has been uploaded'); + }) + builder.addCase(uploadCsv.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }) + + }, +}) + +// Action creators are generated for each case reducer function + export const { setRefetch } = usersSlice.actions + +export default usersSlice.reducer diff --git a/frontend/src/stores/usersSlice.ts b/frontend/src/stores/usersSlice.ts new file mode 100644 index 0000000..c03ffc9 --- /dev/null +++ b/frontend/src/stores/usersSlice.ts @@ -0,0 +1,117 @@ +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' +import axios from 'axios' + +interface MainState { + users: any + loading: boolean + notify: { + showNotification: boolean + textNotification: string + typeNotification: string + } +} + +const initialState: MainState = { + users: [], + loading: false, + notify: { + showNotification: false, + textNotification: '', + typeNotification: 'warn', + }, +} + +export const fetch = createAsyncThunk('users/fetch', async (data: any) => { + const { id, query } = data + const result = await axios.get( + `users${ + query || (id ? `/${id}` : '') + }` + ) + return id ? result.data : result.data.rows +}) + +export const deleteItem = createAsyncThunk('users/deleteUser', async (id: string, thunkAPI) => { + try { + await axios.delete(`users/${id}`) + thunkAPI.dispatch(fetch({ id: '', query: '' })) + } catch (error) { + console.log(error) + } + + // showNotification('Users has been deleted', 'success'); +}) + +export const create = createAsyncThunk('users/createUser', async (data: any) => { + const result = await axios.post( + 'users', + { data } + ) + // showNotification('Users has been created', 'success'); + return result.data +}) + +export const update = createAsyncThunk('users/updateUser', async (payload: any) => { + const result = await axios.put( + `users/${payload.id}`, + { id: payload.id, data: payload.data } + ) + return result.data +}) + +export const usersSlice = createSlice({ + name: 'users', + initialState, + reducers: {}, + extraReducers: (builder) => { + builder.addCase(fetch.pending, (state) => { + state.loading = true + }) + builder.addCase(fetch.rejected, (state) => { + state.loading = false + }) + + builder.addCase(fetch.fulfilled, (state, action) => { + state.users = action.payload + state.loading = false + }) + + builder.addCase(deleteItem.pending, (state) => { + state.loading = true + }) + + builder.addCase(deleteItem.fulfilled, (state) => { + state.loading = false + }) + + builder.addCase(deleteItem.rejected, (state) => { + state.loading = false + }) + + builder.addCase(create.pending, (state) => { + state.loading = true + }) + builder.addCase(create.rejected, (state) => { + state.loading = false + }) + + builder.addCase(create.fulfilled, (state) => { + state.loading = false + }) + + builder.addCase(update.pending, (state) => { + state.loading = true + }) + builder.addCase(update.fulfilled, (state) => { + state.loading = false + }) + builder.addCase(update.rejected, (state) => { + state.loading = false + }) + }, +}) + +// Action creators are generated for each case reducer function +// export const { } = usersSlice.actions + +export default usersSlice.reducer diff --git a/frontend/src/styles.ts b/frontend/src/styles.ts new file mode 100644 index 0000000..299b1b5 --- /dev/null +++ b/frontend/src/styles.ts @@ -0,0 +1,103 @@ +interface StyleObject { + aside: string + asideScrollbars: string + asideBrand: string + asideMenuItem: string + asideMenuItemActive: string + asideMenuDropdown: string + navBarItemLabel: string + navBarItemLabelHover: string + navBarItemLabelActiveColor: string + overlay: string + activeLinkColor: string; + bgLayoutColor: string; + iconsColor: string; + cardsColor: string; + focusRingColor: string; + corners: string; + cardsStyle: string; + linkColor: string; + websiteHeder: string; + borders: string; + shadow: string; + websiteSectionStyle: string; + textSecondary: string; +} + +export const white: StyleObject = { + aside: 'bg-white dark:text-white', + asideScrollbars: 'aside-scrollbars-light', + asideBrand: '', + asideMenuItem: 'text-gray-700 hover:bg-gray-100/70 dark:text-dark-500 dark:hover:text-white dark:hover:bg-dark-800', + asideMenuItemActive: 'font-bold text-black dark:text-white', + asideMenuDropdown: 'bg-gray-100/75', + navBarItemLabel: 'text-blue-600', + navBarItemLabelHover: 'hover:text-black', + navBarItemLabelActiveColor: 'text-black', + overlay: 'from-white via-gray-100 to-white', + activeLinkColor: 'bg-gray-100/70', + bgLayoutColor: 'bg-gray-50', + iconsColor: 'text-blue-500', + cardsColor: 'bg-white', + focusRingColor: 'focus:ring focus:ring-blue-600 focus:border-blue-600 focus:outline-none border-gray-300 dark:focus:ring-blue-600 dark:focus:border-blue-600', + corners: 'rounded', + cardsStyle: 'bg-white border border-brand-400', + linkColor: 'text-blue-600', + websiteHeder: 'border-b border-gray-200', + borders: 'border-gray-200', + shadow: '', + websiteSectionStyle: '', + textSecondary: 'text-gray-500', +} + +export const dataGridStyles = { + '& .MuiDataGrid-cell': { + paddingX: 3, + border: 'none', + }, + '& .MuiDataGrid-columnHeader': { + paddingX: 3, + }, + '& .MuiDataGrid-columnHeaderCheckbox': { + paddingX: 0, + }, + '& .MuiDataGrid-columnHeaders': { + paddingY: 4, + borderStartStartRadius: 7, + borderStartEndRadius: 7, + }, + '& .MuiDataGrid-footerContainer': { + paddingY: 0.5, + borderEndStartRadius: 7, + borderEndEndRadius: 7, + }, + '& .MuiDataGrid-root': { + border: 'none', + }, +}; + +export const basic: StyleObject = { + aside: 'bg-gray-800', + asideScrollbars: 'aside-scrollbars-gray', + asideBrand: 'bg-gray-900 text-white', + asideMenuItem: 'text-gray-300 hover:text-white', + asideMenuItemActive: 'font-bold text-white', + asideMenuDropdown: 'bg-gray-700/50', + navBarItemLabel: 'text-black', + navBarItemLabelHover: 'hover:text-blue-500', + navBarItemLabelActiveColor: 'text-blue-600', + overlay: 'from-gray-700 via-gray-900 to-gray-700', + activeLinkColor: 'bg-gray-100/70', + bgLayoutColor: 'bg-gray-50', + iconsColor: 'text-blue-500', + cardsColor: 'bg-white', + focusRingColor: 'focus:ring focus:ring-blue-600 focus:border-blue-600 focus:outline-none dark:focus:ring-blue-600 border-gray-300 dark:focus:border-blue-600', + corners: 'rounded', + cardsStyle: 'bg-white border border-brand-400', + linkColor: 'text-black', + websiteHeder: '', + borders: '', + shadow: '', + websiteSectionStyle: '', + textSecondary: '', +} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..447db06 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,108 @@ +/* eslint-env node */ +/* eslint-disable-next-line */ +const plugin = require('tailwindcss/plugin') + +module.exports = { + content: [ + './src/**/*.{js,ts,jsx,tsx}', + ], + darkMode: 'class', // or 'media' or 'class' + theme: { + asideScrollbars: { + light: 'light', + gray: 'gray' + }, + extend: { + zIndex: { + '-1': '-1' + }, + flexGrow: { + 5: '5' + }, + maxHeight: { + 'screen-menu': 'calc(100vh - 3.5rem)', + modal: 'calc(100vh - 160px)' + }, + transitionProperty: { + position: 'right, left, top, bottom, margin, padding', + textColor: 'color' + }, + keyframes: { + 'fade-out': { + from: { opacity: 1 }, + to: { opacity: 0 } + }, + 'fade-in': { + from: { opacity: 0 }, + to: { opacity: 1 } + } + }, + animation: { + 'fade-out': 'fade-out 250ms ease-in-out', + 'fade-in': 'fade-in 250ms ease-in-out' + }, + colors: { + dark: { + 900: '#131618', + 800: '#21242A', + 700: '#2C2F36', + 600: '#9CA3AF', + 500: '#CBD5E1', + }, + green:{ + text: '#45B26B', + }, + 'brand': { + 'blue': '#0162FD', + 'green': '#00B448', + 'orange': '#FFAA00', + 'red': '#F20041', + 900: '#14142A', + 800: '#4E4B66', + 700: '#6E7191', + 600: '#A0A3BD', + 500: '#D9DBE9', + 400: '#EFF0F6', + 300: '#F7F7FC' + }, + }, + borderRadius: { + '3xl': '2rem', + }, + } + }, + plugins: [ + require('@tailwindcss/forms'), + require('@tailwindcss/typography'), + plugin(function ({ matchUtilities, theme }) { + matchUtilities( + { + 'aside-scrollbars': (value) => { + const track = value === 'light' ? '100' : '900'; + const thumb = value === 'light' ? '300' : '600'; + const color = value === 'light' ? 'gray' : value; + + return { + scrollbarWidth: 'thin', + scrollbarColor: `${theme(`colors.${color}.${thumb}`)} ${theme( + `colors.${color}.${track}`, + )}`, + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: theme(`colors.${color}.${track}`), + }, + '&::-webkit-scrollbar-thumb': { + borderRadius: '0.25rem', + backgroundColor: theme(`colors.${color}.${thumb}`), + }, + }; + }, + }, + { values: theme('asideScrollbars') }, + ); + }), + ], +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..5bee8c4 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock new file mode 100644 index 0000000..5c2e352 --- /dev/null +++ b/frontend/yarn.lock @@ -0,0 +1,3608 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/generator@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz" + integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== + dependencies: + "@babel/parser" "^7.26.3" + "@babel/types" "^7.26.3" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-module-imports@^7.16.7": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/parser@^7.25.9", "@babel/parser@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" + integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== + dependencies: + "@babel/types" "^7.26.3" + +"@babel/runtime-corejs3@^7.10.2": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.0.tgz" + integrity sha512-JyXXoCu1N8GLuKc2ii8y5RGma5FMpFeO2nAQIe0Yzrbq+rQnN+sFj47auLblR5ka6aHNGPDgv8G/iI2Grb0ldQ== + dependencies: + core-js-pure "^3.20.2" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.25.9": + version "7.26.4" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz" + integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.3" + "@babel/parser" "^7.26.3" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.3" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.25.9", "@babel/types@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@emotion/babel-plugin@^11.13.5": + version "11.13.5" + resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz" + integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/serialize" "^1.3.3" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^11.13.5", "@emotion/cache@^11.14.0", "@emotion/cache@^11.4.0": + version "11.14.0" + resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz" + integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA== + dependencies: + "@emotion/memoize" "^0.9.0" + "@emotion/sheet" "^1.4.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + stylis "4.2.0" + +"@emotion/hash@^0.9.2": + version "0.9.2" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz" + integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== + +"@emotion/is-prop-valid@^1.3.0": + version "1.3.1" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz" + integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== + dependencies: + "@emotion/memoize" "^0.9.0" + +"@emotion/memoize@^0.9.0": + version "0.9.0" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz" + integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== + +"@emotion/react@^11.11.3", "@emotion/react@^11.8.1": + version "11.14.0" + resolved "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz" + integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.13.5" + "@emotion/cache" "^11.14.0" + "@emotion/serialize" "^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.3.3": + version "1.3.3" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz" + integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA== + dependencies: + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/unitless" "^0.10.0" + "@emotion/utils" "^1.4.2" + csstype "^3.0.2" + +"@emotion/sheet@^1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz" + integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== + +"@emotion/styled@^11.11.0": + version "11.14.0" + resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz" + integrity sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.13.5" + "@emotion/is-prop-valid" "^1.3.0" + "@emotion/serialize" "^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" + "@emotion/utils" "^1.4.2" + +"@emotion/unitless@^0.10.0": + version "0.10.0" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz" + integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== + +"@emotion/use-insertion-effect-with-fallbacks@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz" + integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg== + +"@emotion/utils@^1.4.2": + version "1.4.2" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz" + integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA== + +"@emotion/weak-memoize@^0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" + integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== + +"@eslint/eslintrc@^1.3.2": + version "1.3.2" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz" + integrity sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@floating-ui/core@^1.6.0": + version "1.6.8" + resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz" + integrity sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA== + dependencies: + "@floating-ui/utils" "^0.2.8" + +"@floating-ui/dom@^1.0.1": + version "1.6.12" + resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz" + integrity sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w== + dependencies: + "@floating-ui/core" "^1.6.0" + "@floating-ui/utils" "^0.2.8" + +"@floating-ui/utils@^0.2.8": + version "0.2.8" + resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz" + integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== + +"@humanwhocodes/config-array@^0.10.4": + version "0.10.4" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz" + integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/gitignore-to-minimatch@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz" + integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@kurkle/color@^0.3.0": + version "0.3.4" + resolved "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz" + integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w== + +"@mdi/js@^7.4.47": + version "7.4.47" + resolved "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz" + integrity sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ== + +"@mui/core-downloads-tracker@^5.16.13": + version "5.16.13" + resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.13.tgz" + integrity sha512-xe5RwI0Q2O709Bd2Y7l1W1NIwNmln0y+xaGk5VgX3vDJbkQEqzdfTFZ73e0CkEZgJwyiWgk5HY0l8R4nysOxjw== + +"@mui/material@^5.15.7": + version "5.16.13" + resolved "https://registry.npmjs.org/@mui/material/-/material-5.16.13.tgz" + integrity sha512-FhLDkDPYDzvrWCHFsdXzRArhS4AdYufU8d69rmLL+bwhodPcbm2C7cS8Gq5VR32PsW6aKZb58gvAgvEVaiiJbA== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/core-downloads-tracker" "^5.16.13" + "@mui/system" "^5.16.13" + "@mui/types" "^7.2.15" + "@mui/utils" "^5.16.13" + "@popperjs/core" "^2.11.8" + "@types/react-transition-group" "^4.4.10" + clsx "^2.1.0" + csstype "^3.1.3" + prop-types "^15.8.1" + react-is "^19.0.0" + react-transition-group "^4.4.5" + +"@mui/private-theming@^5.16.13": + version "5.16.13" + resolved "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.13.tgz" + integrity sha512-+s0FklvDvO7j0yBZn19DIIT3rLfub2fWvXGtMX49rG/xHfDFcP7fbWbZKHZMMP/2/IoTRDrZCbY1iP0xZlmuJA== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/utils" "^5.16.13" + prop-types "^15.8.1" + +"@mui/styled-engine@^5.16.13": + version "5.16.13" + resolved "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.13.tgz" + integrity sha512-2XNHEG8/o1ucSLhTA9J+HIIXjzlnEc0OV7kneeUQ5JukErPYT2zc6KYBDLjlKWrzQyvnQzbiffjjspgHUColZg== + dependencies: + "@babel/runtime" "^7.23.9" + "@emotion/cache" "^11.13.5" + csstype "^3.1.3" + prop-types "^15.8.1" + +"@mui/system@^5.16.13": + version "5.16.13" + resolved "https://registry.npmjs.org/@mui/system/-/system-5.16.13.tgz" + integrity sha512-JnO3VH3yNoAmgyr44/2jiS1tcNwshwAqAaG5fTEEjHQbkuZT/mvPYj2GC1cON0zEQ5V03xrCNl/D+gU9AXibpw== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/private-theming" "^5.16.13" + "@mui/styled-engine" "^5.16.13" + "@mui/types" "^7.2.15" + "@mui/utils" "^5.16.13" + clsx "^2.1.0" + csstype "^3.1.3" + prop-types "^15.8.1" + +"@mui/types@^7.2.15": + version "7.2.20" + resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.20.tgz" + integrity sha512-straFHD7L8v05l/N5vcWk+y7eL9JF0C2mtph/y4BPm3gn2Eh61dDwDB65pa8DLss3WJfDXYC7Kx5yjP0EmXpgw== + +"@mui/utils@^5.14.16", "@mui/utils@^5.16.13": + version "5.16.13" + resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.16.13.tgz" + integrity sha512-35kLiShnDPByk57Mz4PP66fQUodCFiOD92HfpW6dK9lc7kjhZsKHRKeYPgWuwEHeXwYsCSFtBCW4RZh/8WT+TQ== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/types" "^7.2.15" + "@types/prop-types" "^15.7.12" + clsx "^2.1.1" + prop-types "^15.8.1" + react-is "^19.0.0" + +"@mui/x-data-grid@^6.19.2": + version "6.20.4" + resolved "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.20.4.tgz" + integrity sha512-I0JhinVV4e25hD2dB+R6biPBtpGeFrXf8RwlMPQbr9gUggPmPmNtWKo8Kk2PtBBMlGtdMAgHWe7PqhmucUxU1w== + dependencies: + "@babel/runtime" "^7.23.2" + "@mui/utils" "^5.14.16" + clsx "^2.0.0" + prop-types "^15.8.1" + reselect "^4.1.8" + +"@next/env@14.2.22": + version "14.2.22" + resolved "https://registry.npmjs.org/@next/env/-/env-14.2.22.tgz" + integrity sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q== + +"@next/eslint-plugin-next@13.0.4": + version "13.0.4" + resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.4.tgz" + integrity sha512-jZ4urKT+aO9QHm3ttihrIQscQISDSKK8isAom750+EySn9o3LCSkTdPGBtvBqY7Yku+NqhfQempR5J58DqTaVg== + dependencies: + glob "7.1.7" + +"@next/swc-darwin-arm64@14.2.22": + version "14.2.22" + resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.22.tgz" + integrity sha512-HUaLiehovgnqY4TMBZJ3pDaOsTE1spIXeR10pWgdQVPYqDGQmHJBj3h3V6yC0uuo/RoY2GC0YBFRkOX3dI9WVQ== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" + +"@popperjs/core@^2.11.6", "@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + +"@reduxjs/toolkit@^2.1.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.0.tgz" + integrity sha512-awNe2oTodsZ6LmRqmkFhtb/KH03hUhxOamEQy411m3Njj3BbFvoBovxo4Q1cBWnV1ErprVj9MlF0UPXkng0eyg== + dependencies: + immer "^10.0.3" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.1.0" + +"@restart/hooks@^0.4.7": + version "0.4.16" + resolved "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz" + integrity sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w== + dependencies: + dequal "^2.0.3" + +"@rushstack/eslint-patch@^1.1.3": + version "1.1.4" + resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz" + integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA== + +"@seznam/compose-react-refs@^1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.6.tgz" + integrity sha512-izzOXQfeQLonzrIQb8u6LQ8dk+ymz3WXTIXjvOlTXHq6sbzROg3NWU+9TTAOpEoK9Bth24/6F/XrfHJ5yR5n6Q== + +"@swc/counter@^0.1.3": + version "0.1.3" + resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz" + integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== + +"@swc/helpers@0.5.5": + version "0.5.5" + resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz" + integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A== + dependencies: + "@swc/counter" "^0.1.3" + tslib "^2.4.0" + +"@tailwindcss/forms@^0.5.7": + version "0.5.9" + resolved "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz" + integrity sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg== + dependencies: + mini-svg-data-uri "^1.2.3" + +"@tailwindcss/line-clamp@^0.4.4": + version "0.4.4" + resolved "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz" + integrity sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g== + +"@tailwindcss/typography@^0.5.13": + version "0.5.15" + resolved "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz" + integrity sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA== + dependencies: + lodash.castarray "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + postcss-selector-parser "6.0.10" + +"@tinymce/tinymce-react@^4.3.2": + version "4.3.2" + resolved "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-4.3.2.tgz" + integrity sha512-wJHZhPf2Mk3yTtdVC/uIGh+kvDgKuTw/qV13uzdChTNo68JI1l7jYMrSQOpyimDyn5LHAw0E1zFByrm1WHAVeA== + dependencies: + prop-types "^15.6.2" + tinymce "^6.0.0 || ^5.5.1" + +"@types/date-arithmetic@*": + version "4.1.4" + resolved "https://registry.npmjs.org/@types/date-arithmetic/-/date-arithmetic-4.1.4.tgz" + integrity sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw== + +"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1": + version "3.3.1" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/node@18.7.16": + version "18.7.16" + resolved "https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz" + integrity sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg== + +"@types/numeral@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@types/numeral/-/numeral-2.0.2.tgz" + integrity sha512-A8F30k2gYJ/6e07spSCPpkuZu79LCnkPTvqmIWQzNGcrzwFKpVOydG41lNt5wZXjSI149qjyzC2L1+F2PD/NUA== + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/prop-types@*", "@types/prop-types@^15.7.12": + version "15.7.14" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz" + integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== + +"@types/react-big-calendar@^1.8.8": + version "1.16.0" + resolved "https://registry.npmjs.org/@types/react-big-calendar/-/react-big-calendar-1.16.0.tgz" + integrity sha512-1w2GXAJWlGmaPZOd9J9cyWA/XBNOGRZ4MmRNypEQhwEMIIL9cfd1UdcvzSrQsnBm0qYF/scqmsISNbUzPBE1vg== + dependencies: + "@types/date-arithmetic" "*" + "@types/prop-types" "*" + "@types/react" "*" + +"@types/react-redux@^7.1.24": + version "7.1.24" + resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz" + integrity sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + +"@types/react-transition-group@^4.4.0", "@types/react-transition-group@^4.4.10": + version "4.4.12" + resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz" + integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== + +"@types/react@*", "@types/react@>=16.9.11": + version "18.0.19" + resolved "https://registry.npmjs.org/@types/react/-/react-18.0.19.tgz" + integrity sha512-BDc3Q+4Q3zsn7k9xZrKfjWyJsSlEDMs38gD1qp2eDazLCdcPqAT+vq1ND+Z8AGel/UiwzNUk8ptpywgNQcJ1MQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/use-sync-external-store@^0.0.3": + version "0.0.3" + resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz" + integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== + +"@types/warning@^3.0.0": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz" + integrity sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q== + +"@typescript-eslint/eslint-plugin@^5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz" + integrity sha512-Fde6W0IafXktz1UlnhGkrrmnnGpAo1kyX7dnyHHVrmwJOn72Oqm3eYtddrpOwwel2W8PAK9F3pIL5S+lfoM0og== + dependencies: + "@typescript-eslint/scope-manager" "5.37.0" + "@typescript-eslint/type-utils" "5.37.0" + "@typescript-eslint/utils" "5.37.0" + debug "^4.3.4" + functional-red-black-tree "^1.0.1" + ignore "^5.2.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.37.0", "@typescript-eslint/parser@^5.42.0": + version "5.43.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.43.0.tgz" + integrity sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug== + dependencies: + "@typescript-eslint/scope-manager" "5.43.0" + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/typescript-estree" "5.43.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.37.0.tgz" + integrity sha512-F67MqrmSXGd/eZnujjtkPgBQzgespu/iCZ+54Ok9X5tALb9L2v3G+QBSoWkXG0p3lcTJsL+iXz5eLUEdSiJU9Q== + dependencies: + "@typescript-eslint/types" "5.37.0" + "@typescript-eslint/visitor-keys" "5.37.0" + +"@typescript-eslint/scope-manager@5.43.0": + version "5.43.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz" + integrity sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw== + dependencies: + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/visitor-keys" "5.43.0" + +"@typescript-eslint/type-utils@5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.37.0.tgz" + integrity sha512-BSx/O0Z0SXOF5tY0bNTBcDEKz2Ec20GVYvq/H/XNKiUorUFilH7NPbFUuiiyzWaSdN3PA8JV0OvYx0gH/5aFAQ== + dependencies: + "@typescript-eslint/typescript-estree" "5.37.0" + "@typescript-eslint/utils" "5.37.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.37.0.tgz" + integrity sha512-3frIJiTa5+tCb2iqR/bf7XwU20lnU05r/sgPJnRpwvfZaqCJBrl8Q/mw9vr3NrNdB/XtVyMA0eppRMMBqdJ1bA== + +"@typescript-eslint/types@5.43.0": + version "5.43.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.43.0.tgz" + integrity sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg== + +"@typescript-eslint/typescript-estree@5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.37.0.tgz" + integrity sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA== + dependencies: + "@typescript-eslint/types" "5.37.0" + "@typescript-eslint/visitor-keys" "5.37.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@5.43.0": + version "5.43.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz" + integrity sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg== + dependencies: + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/visitor-keys" "5.43.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.37.0.tgz" + integrity sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.37.0" + "@typescript-eslint/types" "5.37.0" + "@typescript-eslint/typescript-estree" "5.37.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.37.0": + version "5.37.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.37.0.tgz" + integrity sha512-Hp7rT4cENBPIzMwrlehLW/28EVCOcE9U1Z1BQTc8EA8v5qpr7GRGuG+U58V5tTY48zvUOA3KHvw3rA8tY9fbdA== + dependencies: + "@typescript-eslint/types" "5.37.0" + eslint-visitor-keys "^3.3.0" + +"@typescript-eslint/visitor-keys@5.43.0": + version "5.43.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz" + integrity sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg== + dependencies: + "@typescript-eslint/types" "5.43.0" + eslint-visitor-keys "^3.3.0" + +"@vtaits/use-lazy-ref@^0.1.3": + version "0.1.3" + resolved "https://registry.npmjs.org/@vtaits/use-lazy-ref/-/use-lazy-ref-0.1.3.tgz" + integrity sha512-ZTLuFBHSivPcgWrwkXe5ExVt6R3/ybD+N0yFPy4ClzCztk/9bUD/1udKQ/jd7eCal+lapSrRWXbffqI9jkpDlg== + +"@yr/monotone-cubic-spline@^1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz" + integrity sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +apexcharts@^3.45.2: + version "3.54.1" + resolved "https://registry.npmjs.org/apexcharts/-/apexcharts-3.54.1.tgz" + integrity sha512-E4et0h/J1U3r3EwS/WlqJCQIbepKbp6wGUmaAwJOMjHUP4Ci0gxanLa7FR3okx6p9coi4st6J853/Cb1NP0vpA== + dependencies: + "@yr/monotone-cubic-spline" "^1.0.3" + svg.draggable.js "^2.2.2" + svg.easing.js "^2.0.0" + svg.filter.js "^2.0.2" + svg.pathmorphing.js "^0.1.3" + svg.resize.js "^1.4.3" + svg.select.js "^3.0.1" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^4.2.2: + version "4.2.2" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz" + integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + +array-includes@^3.1.4, array-includes@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flat@^1.2.5: + version "1.3.1" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz" + integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + +ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" + integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +autoprefixer@^10.4.0: + version "10.4.9" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.9.tgz" + integrity sha512-Uu67eduPEmOeA0vyJby5ghu1AAELCCNSsLAjK+lz6kYzNM5sqnBO36MqfsjhPjQF/BaJM5U/UuFYyl7PavY/wQ== + dependencies: + browserslist "^4.21.3" + caniuse-lite "^1.0.30001394" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +axe-core@^4.4.3: + version "4.4.3" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz" + integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== + +axios@^1.6.7: + version "1.7.9" + resolved "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz" + integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axobject-query@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz" + integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== + +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.21.3: + version "4.21.3" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== + dependencies: + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" + node-releases "^2.0.6" + update-browserslist-db "^1.0.5" + +buffer-equal-constant-time@1.0.1: + 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== + +busboy@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001394, caniuse-lite@^1.0.30001579: + version "1.0.30001690" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz" + integrity sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chart.js@^4.4.1: + version "4.4.7" + resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz" + integrity sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw== + dependencies: + "@kurkle/color" "^0.3.0" + +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + 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" + optionalDependencies: + fsevents "~2.3.2" + +chroma-js@^2.4.2: + version "2.6.0" + resolved "https://registry.npmjs.org/chroma-js/-/chroma-js-2.6.0.tgz" + integrity sha512-BLHvCB9s8Z1EV4ethr6xnkl/P2YRFOGqfgvuMG/MyCbZPrTA+NeiByY6XvgF0zP4/2deU2CXnWyMa3zu1LqQ3A== + +classnames@^2.2.6: + version "2.5.1" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + +clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +clsx@^2.0.0, clsx@^2.1.0, clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +convert-source-map@^1.5.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +core-js-pure@^3.20.2: + version "3.25.1" + resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.1.tgz" + integrity sha512-7Fr74bliUDdeJCBMxkkIuQ4xfxn/SwrVg+HkJUAoNEXVqYLv55l6Af0dJ5Lq2YBUW9yKqSkLXaS5SYPK6MGa/A== + +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^3.0.2, csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +date-arithmetic@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz" + integrity sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg== + +date-fns@^2.30.0: + version "2.30.0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" + +dayjs@^1.11.10, dayjs@^1.11.7: + version "1.11.13" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz" + integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ== + +deep-is@^0.1.3: + 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== + +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +electron-to-chromium@^1.4.202: + version "1.4.248" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.248.tgz" + integrity sha512-qShjzEYpa57NnhbW2K+g+Fl+eNoDvQ7I+2MRwWnU6Z6F0HhXekzsECCLv+y2OJUsRodjqoSfwHkIX42VUFtUzg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +enhanced-resolve@^5.10.0: + version "5.10.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz" + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.4: + version "1.20.4" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-next@^13.0.4: + version "13.0.4" + resolved "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.0.4.tgz" + integrity sha512-moEC7BW2TK7JKq3QfnaauqRjWzVcEf71gp5DbClpFPHM6QXE0u0uVvSTiHlmOgtCe1vyWAO+AhF87ZITd8mIDw== + dependencies: + "@next/eslint-plugin-next" "13.0.4" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.42.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^3.5.2" + eslint-plugin-import "^2.26.0" + eslint-plugin-jsx-a11y "^6.5.1" + eslint-plugin-react "^7.31.7" + eslint-plugin-react-hooks "^4.5.0" + +eslint-config-prettier@^8.5.0: + version "8.5.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-import-resolver-typescript@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.2.tgz" + integrity sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.10.0" + get-tsconfig "^4.2.0" + globby "^13.1.2" + is-core-module "^2.10.0" + is-glob "^4.0.3" + synckit "^0.8.4" + +eslint-module-utils@^2.7.3: + version "2.7.4" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.26.0: + version "2.26.0" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== + dependencies: + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.3" + has "^1.0.3" + is-core-module "^2.8.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.5" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-jsx-a11y@^6.5.1: + version "6.6.1" + resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz" + integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== + dependencies: + "@babel/runtime" "^7.18.9" + aria-query "^4.2.2" + array-includes "^3.1.5" + ast-types-flow "^0.0.7" + axe-core "^4.4.3" + axobject-query "^2.2.0" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.3.2" + language-tags "^1.0.5" + minimatch "^3.1.2" + semver "^6.3.0" + +eslint-plugin-react-hooks@^4.5.0: + version "4.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + +eslint-plugin-react@^7.31.7: + version "7.31.8" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz" + integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw== + dependencies: + array-includes "^3.1.5" + array.prototype.flatmap "^1.3.0" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.5" + object.fromentries "^2.0.5" + object.hasown "^1.1.1" + object.values "^1.1.5" + prop-types "^15.8.1" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.7" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.23.1: + version "8.23.1" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz" + integrity sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg== + dependencies: + "@eslint/eslintrc" "^1.3.2" + "@humanwhocodes/config-array" "^0.10.4" + "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" + "@humanwhocodes/module-importer" "^1.0.1" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.1" + globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + 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== + +fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + 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== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-saver@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz" + integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +filter-obj@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz" + integrity sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng== + +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formik@^2.4.5: + version "2.4.6" + resolved "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz" + integrity sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g== + dependencies: + "@types/hoist-non-react-statics" "^3.3.1" + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.21" + lodash-es "^4.17.21" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^2.0.0" + +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functional-red-black-tree@^1.0.1: + 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== + +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-tsconfig@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz" + integrity sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1, glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + 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" + +glob@^7.1.3, glob@7.1.7: + version "7.1.7" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globalize@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/globalize/-/globalize-0.1.1.tgz" + integrity sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA== + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.15.0: + version "13.17.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + dependencies: + type-fest "^0.20.2" + +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.2: + version "13.1.2" + resolved "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz" + integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +graceful-fs@^4.2.11, graceful-fs@^4.2.4: + 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== + +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +immer@^10.0.3: + version "10.1.1" + resolved "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +intro.js-react@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/intro.js-react/-/intro.js-react-1.0.0.tgz" + integrity sha512-zR8pbTyX20RnCZpJMc0nuHBpsjcr1wFkj3ZookV6Ly4eE/LGpFTQwPsaA61Cryzwiy/tTFsusf4hPU9NpI9UOg== + +intro.js@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/intro.js/-/intro.js-7.2.0.tgz" + integrity sha512-qbMfaB70rOXVBceIWNYnYTpVTiZsvQh/MIkfdQbpA9di9VBfj1GigUPfcCv3aOfsbrtPcri8vTLTA4FcEDcHSQ== + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.10.0, is-core-module@^2.16.0, is-core-module@^2.8.1, is-core-module@^2.9.0: + version "2.16.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + 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== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jiti@^1.21.6: + version "1.21.7" + resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz" + integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== + +js-sdsl@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz" + integrity sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +jsonwebtoken@^9.0.2: + version "9.0.2" + resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + 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" + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: + version "3.3.3" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.3" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + +krustykrab@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/krustykrab/-/krustykrab-1.1.0.tgz" + integrity sha512-xpX9MPbw+nJseewe6who9Oq46RQwrBfps+dO/N4fSjJhsf2+y4XWC2kz46oBGX8yzMHyYJj35ug0X5s5yxB6tA== + +language-subtag-registry@~0.3.2: + version "0.3.22" + resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + +language-tags@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz" + integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== + dependencies: + language-subtag-registry "~0.3.2" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@^3.0.0, lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz" + integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + 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== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + 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== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +luxon@^3.2.1: + version "3.5.0" + resolved "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== + +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mini-svg-data-uri@^1.2.3: + version "1.4.4" + resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz" + integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== + +minimatch@^3.0.4, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +moment-timezone@^0.5.40: + version "0.5.46" + resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.46.tgz" + integrity sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw== + dependencies: + moment "^2.29.4" + +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +ms@^2.1.1, ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.6, nanoid@^3.3.7: + version "3.3.8" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +next@^14.1.0: + version "14.2.22" + resolved "https://registry.npmjs.org/next/-/next-14.2.22.tgz" + integrity sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw== + dependencies: + "@next/env" "14.2.22" + "@swc/helpers" "0.5.5" + busboy "1.6.0" + caniuse-lite "^1.0.30001579" + graceful-fs "^4.2.11" + postcss "8.4.31" + styled-jsx "5.1.1" + optionalDependencies: + "@next/swc-darwin-arm64" "14.2.22" + "@next/swc-darwin-x64" "14.2.22" + "@next/swc-linux-arm64-gnu" "14.2.22" + "@next/swc-linux-arm64-musl" "14.2.22" + "@next/swc-linux-x64-gnu" "14.2.22" + "@next/swc-linux-x64-musl" "14.2.22" + "@next/swc-win32-arm64-msvc" "14.2.22" + "@next/swc-win32-ia32-msvc" "14.2.22" + "@next/swc-win32-x64-msvc" "14.2.22" + +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +numeral@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz" + integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz" + integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.fromentries@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz" + integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.hasown@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz" + integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.19.5" + +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^8.4.0: + version "8.4.0" + resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +package-json-from-dist@^1.0.0: + 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== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +postcss-import@^14.1.0: + version "14.1.0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz" + integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-nested@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== + dependencies: + postcss-selector-parser "^6.1.1" + +postcss-selector-parser@^6.1.1: + version "6.1.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@^6.1.2: + version "6.1.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@6.0.10: + version "6.0.10" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.4, postcss@^8.4.47: + version "8.4.49" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +postcss@8.4.31: + version "8.4.31" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^3.2.4: + version "3.4.2" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz" + integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== + +prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +query-string@^8.1.0: + version "8.2.0" + resolved "https://registry.npmjs.org/query-string/-/query-string-8.2.0.tgz" + integrity sha512-tUZIw8J0CawM5wyGBiDOAp7ObdRQh4uBor/fUR9ZjmbZVvw95OD9If4w3MQxr99rg0DJZ/9CIORcpEqU5hQG7g== + dependencies: + decode-uri-component "^0.4.1" + filter-obj "^5.1.0" + split-on-first "^3.0.0" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +react-apexcharts@^1.4.1: + version "1.7.0" + resolved "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.7.0.tgz" + integrity sha512-03oScKJyNLRf0Oe+ihJxFZliBQM9vW3UWwomVn4YVRTN1jsIR58dLWt0v1sb8RwJVHDMbeHiKQueM0KGpn7nOA== + dependencies: + prop-types "^15.8.1" + +react-big-calendar@^1.10.3: + version "1.17.1" + resolved "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.17.1.tgz" + integrity sha512-LltUAMSGODWQBKx4013bRe6R0jaINV9hrs970+F860KedpozwRGGMT66esV9mA3mAhfSKoazF/QH1WCyLkXYZA== + dependencies: + "@babel/runtime" "^7.20.7" + clsx "^1.2.1" + date-arithmetic "^4.1.0" + dayjs "^1.11.7" + dom-helpers "^5.2.1" + globalize "^0.1.1" + invariant "^2.2.4" + lodash "^4.17.21" + lodash-es "^4.17.21" + luxon "^3.2.1" + memoize-one "^6.0.0" + moment "^2.29.4" + moment-timezone "^0.5.40" + prop-types "^15.8.1" + react-overlays "^5.2.1" + uncontrollable "^7.2.1" + +react-chartjs-2@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz" + integrity sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA== + +react-datepicker@^4.10.0: + version "4.25.0" + resolved "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz" + integrity sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg== + dependencies: + "@popperjs/core" "^2.11.8" + classnames "^2.2.6" + date-fns "^2.30.0" + prop-types "^15.7.2" + react-onclickoutside "^6.13.0" + react-popper "^2.3.0" + +react-dom@^19.0.0: + version "19.0.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz" + integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ== + dependencies: + scheduler "^0.25.0" + +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + +react-fast-compare@^3.0.1: + version "3.2.2" + resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +react-is@^19.0.0: + version "19.0.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz" + integrity sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-onclickoutside@^6.13.0: + version "6.13.1" + resolved "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.1.tgz" + integrity sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w== + +react-overlays@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz" + integrity sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA== + dependencies: + "@babel/runtime" "^7.13.8" + "@popperjs/core" "^2.11.6" + "@restart/hooks" "^0.4.7" + "@types/warning" "^3.0.0" + dom-helpers "^5.2.0" + prop-types "^15.7.2" + uncontrollable "^7.2.1" + warning "^4.0.3" + +react-popper@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz" + integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + +react-redux@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz" + integrity sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA== + dependencies: + "@babel/runtime" "^7.12.1" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/use-sync-external-store" "^0.0.3" + hoist-non-react-statics "^3.3.2" + react-is "^18.0.0" + use-sync-external-store "^1.0.0" + +react-select-async-paginate@^0.7.9: + version "0.7.9" + resolved "https://registry.npmjs.org/react-select-async-paginate/-/react-select-async-paginate-0.7.9.tgz" + integrity sha512-oGdmXWatj06MNUcLwFkyt8qLBA0km0gFvDF52qaQQEArRjprbTajaYwhtjVotKTw1IXfw3fiq3G9Lgsjfpjz/Q== + dependencies: + "@seznam/compose-react-refs" "^1.0.6" + "@vtaits/use-lazy-ref" "^0.1.3" + krustykrab "^1.1.0" + sleep-promise "^9.1.0" + use-is-mounted-ref "^1.5.0" + use-latest "^1.3.0" + +react-select@^5.7.0: + version "5.9.0" + resolved "https://registry.npmjs.org/react-select/-/react-select-5.9.0.tgz" + integrity sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw== + dependencies: + "@babel/runtime" "^7.12.0" + "@emotion/cache" "^11.4.0" + "@emotion/react" "^11.8.1" + "@floating-ui/dom" "^1.0.1" + "@types/react-transition-group" "^4.4.0" + memoize-one "^6.0.0" + prop-types "^15.6.0" + react-transition-group "^4.3.0" + use-isomorphic-layout-effect "^1.2.0" + +react-switch@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/react-switch/-/react-switch-7.1.0.tgz" + integrity sha512-4xVeyImZE8QOTDw2FmhWz0iqo2psoRiS7XzdjaZBCIP8Dzo3rT0esHUjLee5WsAPSFXWWl1eVA5arp9n2C6yQA== + dependencies: + prop-types "^15.7.2" + +react-toastify@^9.1.2: + version "9.1.3" + resolved "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz" + integrity sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg== + dependencies: + clsx "^1.1.1" + +react-transition-group@^4.3.0, react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react@^19.0.0: + version "19.0.0" + resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz" + integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== + +redux@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz" + integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA== + dependencies: + "@babel/runtime" "^7.9.2" + +redux@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +reselect@^4.1.8: + version "4.1.8" + resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz" + integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== + +reselect@^5.1.0: + version "5.1.1" + resolved "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz" + integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.1.7, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.8: + version "1.22.10" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.3: + version "2.0.0-next.4" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +scheduler@^0.25.0: + version "0.25.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz" + integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.7, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +sleep-promise@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz" + integrity sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA== + +source-map-js@^1.0.2, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +split-on-first@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz" + integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.matchall@^4.0.7: + version "4.0.7" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.1" + side-channel "^1.0.4" + +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + 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== + +styled-jsx@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz" + integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== + dependencies: + client-only "0.0.1" + +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + +sucrase@^3.35.0: + version "3.35.0" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg.draggable.js@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz" + integrity sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw== + dependencies: + svg.js "^2.0.1" + +svg.easing.js@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz" + integrity sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA== + dependencies: + svg.js ">=2.3.x" + +svg.filter.js@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz" + integrity sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw== + dependencies: + svg.js "^2.2.5" + +svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5, svg.js@>=2.3.x: + version "2.7.1" + resolved "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz" + integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA== + +svg.pathmorphing.js@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz" + integrity sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww== + dependencies: + svg.js "^2.4.0" + +svg.resize.js@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz" + integrity sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw== + dependencies: + svg.js "^2.6.5" + svg.select.js "^2.1.2" + +svg.select.js@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz" + integrity sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ== + dependencies: + svg.js "^2.2.5" + +svg.select.js@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz" + integrity sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw== + dependencies: + svg.js "^2.6.5" + +swr@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz" + integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw== + +synckit@^0.8.4: + version "0.8.4" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz" + integrity sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.4.0" + +tailwindcss@^3.4.1: + version "3.4.17" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz" + integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.6.0" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.2" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.6" + lilconfig "^3.1.3" + micromatch "^4.0.8" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.1.1" + postcss "^8.4.47" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.2" + postcss-nested "^6.2.0" + postcss-selector-parser "^6.1.2" + resolve "^1.22.8" + sucrase "^3.35.0" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + 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== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" + +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +"tinymce@^6.0.0 || ^5.5.1": + version "6.8.5" + resolved "https://registry.npmjs.org/tinymce/-/tinymce-6.8.5.tgz" + integrity sha512-qAL/FxL7cwZHj4BfaF818zeJJizK9jU5IQzTcSLL4Rj5MaJdiVblEj7aDr80VCV1w9h4Lak9hlnALhq/kVtN1g== + +to-regex-range@^5.0.1: + 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== + dependencies: + is-number "^7.0.0" + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typescript@^4.8.3: + version "4.8.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz" + integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +uncontrollable@^7.2.1: + version "7.2.1" + resolved "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + +update-browserslist-db@^1.0.5: + version "1.0.9" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz" + integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +use-is-mounted-ref@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/use-is-mounted-ref/-/use-is-mounted-ref-1.5.0.tgz" + integrity sha512-p5FksHf/ospZUr5KU9ese6u3jp9fzvZ3wuSb50i0y6fdONaHWgmOqQtxR/PUcwi6hnhQDbNxWSg3eTK3N6m+dg== + +use-isomorphic-layout-effect@^1.1.1, use-isomorphic-layout-effect@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz" + integrity sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w== + +use-latest@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz" + integrity sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ== + dependencies: + use-isomorphic-layout-effect "^1.1.1" + +use-sync-external-store@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +warning@^4.0.2, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yaml@^2.3.4: + version "2.7.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..67d93fa --- /dev/null +++ b/nginx.conf @@ -0,0 +1,89 @@ +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + client_max_body_size 10M; + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + server { + listen 8080; + + error_page 502 /502.html; + location = /502.html { + root /usr/share/nginx/html; + internal; + } + + location /api/logError { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://127.0.0.1:3001/api/logError; + } + + + location /api-docs/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://127.0.0.1:3000/api-docs/; + } + + location /api/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range'; + add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range'; + proxy_pass http://127.0.0.1:3000/api/; + } + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://127.0.0.1:3001/; + } + + location /app-shell/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://127.0.0.1:4000/; + } + + location ~ /_next/webpack-hmr { + proxy_pass http://127.0.0.1:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location ~ ^/_next/ { + proxy_pass http://127.0.0.1:3001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..eb89994 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "app", + "version": "0.0.0", +"scripts": { + "build:production": "cd ./frontend && yarn install && yarn run build && rm -rf ./node_modules && cd ../backend && yarn install", + "start:production": "cd ./backend && NODE_ENV=production yarn start" + } + +} diff --git a/pids/backend.pid b/pids/backend.pid new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/pids/backend.pid @@ -0,0 +1 @@ +4 diff --git a/pids/frontend.pid b/pids/frontend.pid new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/pids/frontend.pid @@ -0,0 +1 @@ +3